简述

生产者将数据写入缓冲区,直到它到达缓冲区的末尾,这时,它从开始位置重新启动,覆盖现有数据。消费者线程读取数据并将其写入标准错误。

Wait condition(等待条件)比单独使用 mutex(互斥量)有一个更高级的并发性,如果缓冲区的访问由一个 QMutex 把守,当生产者线程访问缓冲区时,消费者线程将无法访问。然而,两个线程同时访问不同的缓冲区是没有害处的。

示例包含两个类:Producer 和 Consumer,均继承自 QThread。循环缓冲区用于两个类之间的沟通,同步工具用于保护全局变量。

另一种“生产者 - 消费者”模式的方案是使用 QSemaphore - Qt之线程同步(生产者消费者模式 - QSemaphore)

  • 简述
  • 全局变量
  • Producer
  • Consumer
  • main 函数
  • 更多参考

全局变量

首先,一起来看循环缓冲区和相关的同步工具:

const int DataSize = 100000;const int BufferSize = 8192;
char buffer[BufferSize];QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;

DataSize 是生产者将生成的数据数量,为了让示例尽可能地简单,把它定义为一个常数。BufferSize 是循环缓冲区的大小,小于 DataSize,这意味着在某一时刻生产者将达到缓冲区的末尾,并从开始位置重新启动。

要同步生产者和消费者,需要两个 wait 条件和一个 mutex。当生产者生成一些数据时,bufferNotEmpty 条件被发射,告诉消费者可以读取它了;当消费者读取一些数据时,bufferNotFull 条件被发射,告诉生产者生成更多的数据。numUsedBytes 为缓冲区中所包含数据的字节数。

总之,wait 条件、mutex、和 numUsedBytes 计数器确保生产者不会先于消费者超过 BufferSize 的大小,而消费者永远不会读取生产者尚未生成的数据。

Producer

Producer 类的代码如下:

class Producer : public QThread
{
public:Producer(QObject *parent = NULL) : QThread(parent){}void run() Q_DECL_OVERRIDE{qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));for (int i = 0; i < DataSize; ++i) {mutex.lock();if (numUsedBytes == BufferSize)bufferNotFull.wait(&mutex);mutex.unlock();buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];mutex.lock();++numUsedBytes;bufferNotEmpty.wakeAll();mutex.unlock();}}
};

生产者生成 DataSize 字节的数据。在往循环缓冲区写入一个字节之前,它必须先检测缓冲区是否已满(例如,numUsedBytes 等于 BufferSize),如果缓冲区满了,线程就会在 bufferNotFull 条件上等待。

最后,生产者增加 numUsedBytes,并且标志 bufferNotEmpty 条件为 true,从而 numUsedBytes 必然大于 0,

我们使用一个 mutex 保护 numUsedBytes 变量的所有访问。此外,QWaitCondition::wait() 函数接受一个 mutex 作为其参数。当线程被置于休眠状态之前,该 mutex 被解锁;当线程被唤醒,该 mutex 被锁定。此外,为了防止竞争条件发生,从锁定状态到等待状态的过渡具有原子性。

Consumer

现在转向 Consumer 类:

class Consumer : public QThread
{Q_OBJECT
public:Consumer(QObject *parent = NULL) : QThread(parent){}void run() Q_DECL_OVERRIDE{for (int i = 0; i < DataSize; ++i) {mutex.lock();if (numUsedBytes == 0)bufferNotEmpty.wait(&mutex);mutex.unlock();fprintf(stderr, "%c", buffer[i % BufferSize]);mutex.lock();--numUsedBytes;bufferNotFull.wakeAll();mutex.unlock();}fprintf(stderr, "\n");}signals:void stringConsumed(const QString &text);
};

代码非常类似于生产者,在读取字节之前,需要先检查缓冲区是否为空(numUsedBytes 为 0),而非它是否为已满。并且,当它为空时,等待 bufferNotEmpty 条件。在读取字节后,减小 numUsedBytes (而非增加),并标志 bufferNotFull 条件(而非 bufferNotEmpty 条件)。

main() 函数

在 main() 函数中,我们创建两个线程,并调用 QThread::wait(),以确保在退出之前,这两个线程有时间完成。

int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);Producer producer;Consumer consumer;producer.start();consumer.start();producer.wait();consumer.wait();return 0;
}

当运行这个程序时,会发生什么呢?

最初,生产者是唯一一个可以做任何事情的线程,消费者阻塞并等待 bufferNotEmpty 条件被发射(numUsedBytes 是 0)。一旦生产者把一个字节放入缓冲区,numUsedBytes 就会变为 BufferSize - 1,并且 bufferNotEmpty 条件被发射。这时,可能发生两件事:要么消费者线程接管和读取字节,要么生产者开始生成第二个字节。

此示例中提出的“生产者 - 消费者”模式,适用于编写高并发多线程应用。在多处理器计算机中,程序可能比基于 mutex 的方案快达两倍之多,因为两个线程可以同一时间在缓冲区的不同部分处于激活状态。

要知道,这些好处并不总能实现,加锁和解锁一个 QMutex 是需要成本的。在实践中,可能需要把缓冲区分为块,并针对块操作而非单个字节。缓冲区的大小也是一个必须仔细选择的参数,需要基于实验。

Qt之线程同步(生产者消费者模式 - QWaitCondition)相关推荐

  1. Java线程实现生产者—消费者模式

    在这里插入代码片# Java 线程实现生产者-消费者模式 ##思路:实现类似消费者生产者线程之间通讯的功能,每创建一个工人,就让这个工人干活,干一段时间,工人自动消失,然后又去创建一个工人干活: 代码 ...

  2. Java线程实现生产者消费者模式

    1 什么是生产者消费者模式 想一个现实生活中的例子,啤酒商---超市---消费者也就是我们,啤酒商生产了啤酒,然后将啤酒销售给了超市,我们消费之又会到超市将啤酒买回来自己喝,那么啤酒商和消费者之间是什 ...

  3. 13-多线程01 实现多线程 线程同步 生产者消费者

    1.实现多线程 1.1简单了解多线程[理解] 是指从软件或者硬件上实现多个线程并发执行的技术. 具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能. 1.2并发和并行[理解] 并 ...

  4. 线程同步 生产者消费者 java_Java线程同步:生产者-消费者 模型(代码示例)

    public class ThreadSyn { public static void main(String[] args) { new ThreadSyn(); } public ThreadSy ...

  5. Qt 实现的一个生产者消费者模式类

    最近做公司项目遇到一个问题,加载20万的日志文件,解析后写入数据库中.做完后发现读文件.解析.写入数据依次搞下来,速度实在是太慢. 所以学习用多线程的来完成这个工作,考虑用生产者消费者模式来完成. 单 ...

  6. 线程同步-生产者消费者问题

    在进行多线程编程时,难免还要碰到两个问题,那就线程间的互斥与同步: 线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才 ...

  7. 生产者消费者伪码_[线程同步]生产者消费者代码实现

    生产者消费者问题是一个著名的线程同步问题,该问题描述如下: 有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池,生产者 ...

  8. Qt之线程同步(生产者消费者模式 - QSemaphore)

     简述 生产者将数据写入缓冲区,直到它到达缓冲区的末尾,此时,它将从开始位置重新启动,覆盖现有数据.消费者线程读取数据并将其写入标准错误. Semaphore(信号量) 比 mutex(互斥量)有 ...

  9. 一文讲明生产者-消费者模式

    核心是一个任务队列,生产者线程生产任务,并将任务添加到任务队列中,而消费者线程从任务队列中获取任务并执行.该模式有如下优点: 1 解耦 解耦的关键:组件之间的依赖关系和通信方式受限.生产者.消费者之间 ...

最新文章

  1. java查看对象锁级别_对象级别锁 vs 类级别锁(Java)
  2. 闲鱼如何一招保证推荐流稳如泰山
  3. 已知某班学生的英语成绩按学号(从1开始)从小到大的顺序排列在tab表中,要查的学生学号放在变量no中,查表结果放在变量english中。
  4. java jdk 1.8 配置_java_Day01: java的jdk环境变量配置(1.8)
  5. ecshop分页类assign_pager分析和扩展
  6. 第十节:基于MVC5+Unity+EF+Log4Net的基础结构搭建
  7. JAVA内存模型及垃圾回收自我总结
  8. Linux下捕捉信号
  9. 解决“chrome正受到自动测试软件的控制”信息栏显示问题(转)
  10. 微信「扫码支付」被诉侵犯专利权;苹果回应“iOS 13 频繁提醒 App 定位”;Python 2 退休 | 极客头条...
  11. linux向上翻页_Linux下vim编辑器命令大全
  12. 数据结构笔记(十五)-- 数组原理
  13. 专题一 关于Windows10安装问题
  14. psim扰动观察法编程c语言,一种数字PID控制扰动观察法光伏电池MPPT仿真.doc
  15. iOS之 simlator模拟器截屏
  16. 截图快捷键ctrl加什么
  17. Digging Into Self-Supervised Monocular Depth Estimation(2019.8)
  18. flutter-Routers基础介绍
  19. 解决U盘中文件全部变成快捷方式的问题
  20. 华为认证网络工程师考试是中文吗?

热门文章

  1. 服务器漏洞文件被删除漏洞,【华中科技大学 - 漏洞预警】Wordpress = 4.9.6 任意文件删除漏洞...
  2. 导师要让你学会的“显规则”
  3. 分享清华大学鲁志教授实验室生物信息学教程
  4. 「兼容M1」Royal TSX for Mac 最强远程管理软件
  5. 适用于Photoshop的人像美容磨皮ps插件:Beauty Retouch Panel 2021 Mac
  6. Java笔记-AES加解密(PKCS7padding可用)
  7. QML工作笔记-文本输入设置长度以及回显方式(TextInput与TextField通用)
  8. Qt学习笔记-QSS装饰控件
  9. MySQL入门之事件
  10. Hyper-v和VMware 兼容问题