Wait Condition 例子
2015-08-28 20:41
323 查看
翻译自Qt帮助文档:Wait Condition Example
用Qt证明多线程编程。
Wait Condition 例子展示了如何使用 QWaitCondition 和 QMutex 来控制对一个循环缓冲区的访问,生产者线程和消费者线程共享该循环缓冲区。
生产者线程往缓冲区写数据,直到缓冲区的尾部,然后从缓冲区开头重新开始重写已经存在的数据。当数据被生产出来后消费者线程读取数据并写道标准错误中。
Wait Conditions 使高级并发成为可能,比单独使用mutexes的可能性大。如果想访问QMutex控制的缓冲区,消费者线程和生产者线程不能同时访问缓冲区。然而,让两个线程同时访问缓冲区的不同部分是没有什么害处的。
这个例子包括两个类:生产者(Producer)和 消费者(Consumer)。它们都继承了QThread。这两个类通过循环缓冲区交互,保护缓冲区的同步工具设为全局变量。
使用 QWaitCondition 和 QMutex 可以解决生产者-消费者的问题,另一种替代的解决方法就是使用QSemaphore。
全局变量
让我们从循环缓冲区和相关的同步工具开始吧:
DataSize 是生产者将要产生的数据量大小。为了尽量使例子简单些,将它设为常量。
BufferSize是循环缓冲区的大小。它比DataSize小,意味着某一时候生产者将会到达缓冲区的尾部,然后重新回到缓冲区起始端。
为了同步生产者和消费者,我们需要两个 wait condition和一个mutex。当生产者已经产生一些数据时 发送bufferNotEmpty condition 信号,告诉消费者它可以开始读数据了。当消费者已经读到一些数据时 发送 bufferNotFull condition信号,告诉生产者它可以产生更多的数据了。numUsedBytes是缓冲区中包含了数据的字节数。
同时,wait conditions,mutex,以及numUsedBytes计数器 确保了生产者永远不会超过消费者 BufferSize 字节,并且消费者永远不会去读生产者还未产生出来的数据。
生产者类
让我们从生产者的类开始吧:
最后,生产者使用mutex 增加 numUsedBytes 变量值,如果 numUsedBytes > 0 就通知condition bufferNotEmpty 是true。
我们通过mutex控制所有对numUsedBytes 变量的访问。另外,QWaitCondition::wait() 函数将mutex作为实参。在线程进入睡眠状态前解锁mutex,并当线程被唤醒时锁住mutex。而且,从锁住状态转换到等待状态是原子的(即支持多线程并发),是为了竞争条件的产生。
消费者类
让我们转到消费者类:
主函数
在主函数中,我们创建两个线程,并调用QThread::wait()来确保两个线程在退出前有时间完成。
这个例子展示的生产者-消费者模式使那些写出高并发的多线程应用程序称为可能。在一个多处理器机器上,这个程序的效率可能是那些基于mutex的程序两倍,因为处理缓冲区的不同部分时可以运行两个线程。
注意,尽管经常未意识到这些好处,获取和释放一个 QMutex 需要付出一个代价。事实上,这样做是值得的:将缓冲区分成数块(chunks),然后操作块而不是单个字节。基于实验,选取缓冲大小时要留心。
用Qt证明多线程编程。
Wait Condition 例子展示了如何使用 QWaitCondition 和 QMutex 来控制对一个循环缓冲区的访问,生产者线程和消费者线程共享该循环缓冲区。
生产者线程往缓冲区写数据,直到缓冲区的尾部,然后从缓冲区开头重新开始重写已经存在的数据。当数据被生产出来后消费者线程读取数据并写道标准错误中。
Wait Conditions 使高级并发成为可能,比单独使用mutexes的可能性大。如果想访问QMutex控制的缓冲区,消费者线程和生产者线程不能同时访问缓冲区。然而,让两个线程同时访问缓冲区的不同部分是没有什么害处的。
这个例子包括两个类:生产者(Producer)和 消费者(Consumer)。它们都继承了QThread。这两个类通过循环缓冲区交互,保护缓冲区的同步工具设为全局变量。
使用 QWaitCondition 和 QMutex 可以解决生产者-消费者的问题,另一种替代的解决方法就是使用QSemaphore。
全局变量
让我们从循环缓冲区和相关的同步工具开始吧:
const int DataSize = 100000; const int BufferSize = 8192; char buffer[BufferSize]; QWaitCondition bufferNotEmpty; QWaitCondition bufferNotFull; QMutex mutex; int numUsedBytes = 0;
DataSize 是生产者将要产生的数据量大小。为了尽量使例子简单些,将它设为常量。
BufferSize是循环缓冲区的大小。它比DataSize小,意味着某一时候生产者将会到达缓冲区的尾部,然后重新回到缓冲区起始端。
为了同步生产者和消费者,我们需要两个 wait condition和一个mutex。当生产者已经产生一些数据时 发送bufferNotEmpty condition 信号,告诉消费者它可以开始读数据了。当消费者已经读到一些数据时 发送 bufferNotFull condition信号,告诉生产者它可以产生更多的数据了。numUsedBytes是缓冲区中包含了数据的字节数。
同时,wait conditions,mutex,以及numUsedBytes计数器 确保了生产者永远不会超过消费者 BufferSize 字节,并且消费者永远不会去读生产者还未产生出来的数据。
生产者类
让我们从生产者的类开始吧:
class Producer:public QThread { public: Producer(QObject *parent=NULL):QThread(parent) { } void run() { qsrand(QTimer(0,0,0).secsTo(QTime::currentTime())); for(int i=0; i<DataSize; ++i){ mutex.lock(); if(numUsedBytes == BufferSize) { <span style="background-color: rgb(255, 255, 153);">bufferNotFull.wait(&mutex);</span> } mutex.unlock(); buffer[i%BufferSize] = "ACGT"[(int)qrand()%4]; mutex.lock(); ++numUsedBytes; <span style="background-color: rgb(255, 204, 255);">bufferNotEmpty.wakeAll();</span> mutex.unlock(); } } };生产者产生DataSize字节数据。在它往循环缓冲区些一个字节前,它必须先检查缓冲区是否满了(即 numUsedBytes=BufferSize)。如果缓冲区已满,该线程等待 bufferNotFull condition。
最后,生产者使用mutex 增加 numUsedBytes 变量值,如果 numUsedBytes > 0 就通知condition bufferNotEmpty 是true。
我们通过mutex控制所有对numUsedBytes 变量的访问。另外,QWaitCondition::wait() 函数将mutex作为实参。在线程进入睡眠状态前解锁mutex,并当线程被唤醒时锁住mutex。而且,从锁住状态转换到等待状态是原子的(即支持多线程并发),是为了竞争条件的产生。
消费者类
让我们转到消费者类:
class Consumer:public QThread { Q_OBJECT public: Consumer(QObject *parent=NULL):QThread(parent) { } void run() { for(int i=0; i<DataSize; ++i){ mutex.lock(); if(numUsedBytes == 0) { <span style="background-color: rgb(255, 204, 255);">bufferNotEmpty.wait(&mutex)</span>; } mutex.unlock(); fprintf(sederr,"%c",buffer[i%BufferSize]); mutex.lock(); --numUsedBytes; <span style="background-color: rgb(255, 255, 153);"> bufferNotFull.wakeAll();</span> mutex.unlock(); } fprintf(stderr,"\n"); } signals: void stringConsumed(const QString &text); };这个代码与生产者类似。在读字节之前,我们检查缓冲区是否是空的(numUsedBytes =0),而不是检查它是不是满的,如果它时空的就等待 bufferNotEmpty condition。在我们读到字节之后,我们减少 numUsedBytes(而不是增加),然后给 bufferNotFull condition 发信号(而不是 bufferNotEmpty condition)。
主函数
在主函数中,我们创建两个线程,并调用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 condition(numUsedBytes=0)触发。一旦生产者往缓冲区中写入一个字节,numUsedBytes = BufferSize-1,并且bufferNotEmpty condition被触发。同时,有可能发生两件事:要么消费者线程接着执行,要么生产者接着产生第一个字节。
这个例子展示的生产者-消费者模式使那些写出高并发的多线程应用程序称为可能。在一个多处理器机器上,这个程序的效率可能是那些基于mutex的程序两倍,因为处理缓冲区的不同部分时可以运行两个线程。
注意,尽管经常未意识到这些好处,获取和释放一个 QMutex 需要付出一个代价。事实上,这样做是值得的:将缓冲区分成数块(chunks),然后操作块而不是单个字节。基于实验,选取缓冲大小时要留心。
相关文章推荐
- 生产者与消费者 & wait()& notify()
- _itemFailedToPlayToEnd: { kind = 1; new = 2; old = 0; }2
- _itemFailedToPlayToEnd: { kind = 1; new = 2; old = 0; }
- getpagesize.c:32: __getpagesize: Assertion `_rtld_global_ro._dl_pagesize != 0' failed
- android.intent.action.MAIN与android.intent.category
- vim 配置替换错误E488: Trailing characters
- unity3d 游戏人工智能开发之状态机(C#模板与示例)
- 8.28筆記 Failure getting entry for 0x...、fresco獲取bitmap
- SwingUtilities.invokeLater()和SwingUtilities.invokeAndWait()的区别(有什么区别)
- 自定义属性(styleable,TypeArray,obtainStyledAttributes)
- NSSearchPathForDirectoriesInDomains用法
- 如何使用 fail2ban 防御 SSH 服务器的暴力破解攻击
- ripple Failed to load resource: the server responded with a status of 404 (Not Found)
- GridView——MasterDetail设置
- HDU 1022 Train Problem I(栈)
- 使用PopupContainerEdit和PopupContainerControl制作下拉菜单树小记
- codeforces 134B B. Pairs of Numbers(枚举+辗转相除)
- leetcode 217: Contains Duplicate
- AIDLservice
- hdu5319 Painter