QT通过三种形式提供了对线程的支持。它们分别是,一、平台无关的线程类,二、线程安全的事件投递,三、跨线程的信号-槽连接。这使得开发轻巧的多线程Qt程序更为容易,并能充分利用多处理器机器的优势。多线程编程也是一个有用的模式,它用于解决执行较长时间的操作而不至于用户界面失去响应。在Qt的早期版本中,在构建库时有不选择线程支持的选项,从4.0开始,线程总是有效的。

线程类

Qt 包含下面一些线程相关的类:
QThread 提供了开始一个新线程的方法
QThreadStorage 提供逐线程数据存储
QMutex  提供相互排斥的锁,或互斥量
QMutexLocker 是一个便利类,它可以自动对QMutex加锁与解锁
QReadWriterLock 提供了一个可以同时读操作的锁
QReadLocker与QWriteLocker 是便利类,它自动对QReadWriteLock加锁与解锁
QSemaphore 提供了一个整型信号量,是互斥量的泛化
QWaitCondition 提供了一种方法,使得线程可以在被另外线程唤醒之前一直休眠。

创建一个线程

为创建一个线程,子类化QThread并且重写它的run()函数,例如:
class MyThread : public QThread
 {
     Q_OBJECT

protected:
     void run();
 };

void MyThread::run()
 {
     ...
 }
之后,创建这个线程对象的实例,调用QThread::start()。于是,在run()里出现的代码将会在另外线程中被执行。
注意:QCoreApplication::exec()必须总是在主线程(执行main()的那个线程)中被调用,不能从一个QThread中调用。在GUI程序中,主线程也被称为GUI线程,因为它是唯一一个允许执行GUI相关操作的线程。另外,你必须在创建一个QThread之前创建QApplication(or QCoreApplication)对象。

线程同步

QMutex, QReadWriteLock, QSemaphore,  QWaitCondition 提供了线程同步的手段。使用线程的主要想法是希望它们可以尽可能并发执行,而一些关键点上线程之间需要停止或等待。例如,假如两个线程试图同时访问同一个全局变量,结果可能不如所愿。
QMutex  提供相互排斥的锁,或互斥量。在一个时刻至多一个线程拥有mutex,假如一个线程试图访问已经被锁定的mutex,那么它将休眠,直到拥有mutex的线程对此mutex解锁。Mutexes常用来保护共享数据访问。
 QReadWriterLock 与QMutex相似,除了它对 "read","write"访问进行区别对待。它使得多个读者可以共时访问数据。使用QReadWriteLock而不是QMutex,可以使得多线程程序更具有并发性。

QReadWriteLock lock;
 void ReaderThread::run()
 {
    // ...
     lock.lockForRead();
     read_file();
     lock.unlock();
     //...
 }

void WriterThread::run()
 {
    // ...
     lock.lockForWrite();
     write_file();
     lock.unlock();
    // ...
 }

QSemaphore 是QMutex的一般化,它可以保护一定数量的相同资源,与此相对,一个mutex只保护一个资源。下面例子中,使用QSemaphore来控制对环状缓冲的访问,此缓冲区被生产者线程和消费者线程共享。生产者不断向缓冲写入数据直到缓冲末端,再从头开始。消费者从缓冲不断读取数据。信号量比互斥量有更好的并发性,假如我们用互斥量来控制对缓冲的访问,那么生产者,消费者不能同时访问缓冲。然而,我们知道在同一时刻,不同线程访问缓冲的不同部分并没有什么危害。

const int DataSize = 100000;
 const int BufferSize = 8192;
 char buffer[BufferSize];

QSemaphore freeBytes(BufferSize);
 QSemaphore usedBytes;

class Producer : public QThread
 {
 public:
     void run();
 };

void Producer::run()
 {
     qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
     for (int i = 0; i < DataSize; ++i) {
         freeBytes.acquire();
         buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
         usedBytes.release();
     }
 }

class Consumer : public QThread
 {
 public:
     void run();
 };

void Consumer::run()
 {
     for (int i = 0; i < DataSize; ++i) {
         usedBytes.acquire();
         fprintf(stderr, "%c", buffer[i % BufferSize]);
         freeBytes.release();
     }
     fprintf(stderr, "\n");
 }

int main(int argc, char *argv[])
 {
     QCoreApplication app(argc, argv);
     Producer producer;
     Consumer consumer;
     producer.start();
     consumer.start();
     producer.wait();
     consumer.wait();
     return 0;
 }
QWaitCondition 允许线程在某些情况发生时唤醒另外的线程。一个或多个线程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()设置一个条件。wakeOne()随机唤醒一个,wakeAll()唤醒所有。

下面的例子中,生产者首先必须检查缓冲是否已满(numUsedBytes==BufferSize),如果是,线程停下来等待bufferNotFull条件。如果不是,在缓冲中生产数据,增加numUsedBytes,激活条件 bufferNotEmpty。使用mutex来保护对numUsedBytes的访问。另外,QWaitCondition::wait()接收一个mutex作为参数,这个mutex应该被调用线程初始化为锁定状态。在线程进入休眠状态之前,mutex会被解锁。而当线程被唤醒时,mutex会处于锁定状态,而且,从锁定状态到等待状态的转换是原子操作,这阻止了竞争条件的产生。当程序开始运行时,只有生产者可以工作。消费者被阻塞等待bufferNotEmpty条件,一旦生产者在缓冲中放入一个字节,bufferNotEmpty条件被激发,消费者线程于是被唤醒。

const int DataSize = 100000;
 const int BufferSize = 8192;
 char buffer[BufferSize];

QWaitCondition bufferNotEmpty;
 QWaitCondition bufferNotFull;
 QMutex mutex;
 int numUsedBytes = 0;

class Producer : public QThread
 {
 public:
     void run();
 };

void Producer::run()
 {
     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();
     }
 }

class Consumer : public QThread
 {
 public:
     void run();
 };

void Consumer::run()
 {
     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");
 }

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

http://www.cppblog.com/yuanyajie/archive/2007/08/22/30599.html

QWaitCondition(和Java的Notify机制非常相像)的更多相关文章

  1. 通俗的解释JAVA wait/notify机制

    生活中,我们常遇到需要等待的场景,例如去银行办事,在没轮到自己之前需要一直等待,但是如果需要自己每隔几秒钟就去柜台前看看状况,那肯定是种非常低效和令人恼火的体验.而实际的情况是,接待员会让您拿个号,说 ...

  2. 为什么JAVA要提供 wait/notify 机制?是为了避免轮询带来的性能损失

    wait/notify  机制是为了避免轮询带来的性能损失. 为了说清道理,我们用“图书馆借书”这个经典例子来作解释. 一本书同时只能借给一个人.现在有一本书,图书馆已经把这本书借了张三. 在简单的s ...

  3. C# 实现java中 wiat/notify机制

    最近在学习java,看到wiat/notify机制实现线程通信,由于平时工作用的C#,赶紧用C#方式实现一个demo. Java 代码: import java.util.ArrayList; imp ...

  4. java的锁机制

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线 ...

  5. Java的多线程机制系列:(一)总述及基础概念

    前言 这一系列多线程的文章,一方面是个人对Java现有的多线程机制的学习和记录,另一方面是希望能给不熟悉Java多线程机制.或有一定基础但理解还不够深的读者一个比较全面的介绍,旨在使读者对Java的多 ...

  6. Java 类反射机制分析

    Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...

  7. java的锁机制——synchronized

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线 ...

  8. 沉淀再出发:再谈java的多线程机制

    沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于ja ...

  9. Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

    一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

随机推荐

  1. HDU1700:Points on Cycle

    Problem Description There is a cycle with its center on the origin. Now give you a point on the cycl ...

  2. nodejs这个过程POST求

    下面是一个web登陆模拟过程.当我们问一个链接,你得到一个表格,然后填写相应的表格值,然后提交登陆. var http = require('http'); var querystring = req ...

  3. 第十四章 红黑树——C++代码实现

    红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...

  4. 【原创】FAT32系统硬盘的数据恢复实例,这篇文章也是从自己QQ空间的,转移过来的

    FAT32系统硬盘的数据恢复实例软件:win hex(16进制的编辑器)恢复内容:在FAT32中恢复一张图片1.首先我们格式化一个分区为空,让分区的系统选为FAT32系统格式化.如下: psb.jpg ...

  5. Python之路,Day20 - 分布式监控系统开发

    Python之路,Day20 - 分布式监控系统开发   本节内容 为什么要做监控? 常用监控系统设计讨论 监控系统架构设计 监控表结构设计 为什么要做监控? –熟悉IT监控系统的设计原理 –开发一个 ...

  6. ButterKnife 注解

    简介 官网:http://jakewharton.github.io/butterknife/ github:https://github.com/JakeWharton/butterknife 依赖 ...

  7. Directive Definition Object

    不知道为什么这个我并没有想翻译过来的欲望,或许我并没有都看熟透,不好误人子弟,原版奉上. Here's an example directive declared with a Directive D ...

  8. .NET读取Excel

    1.代码 string strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + Path + ";Ext ...

  9. (转)echo和print的区别

    在实际使用中, print 和 echo 两者的功能几乎是完全一样.可以这么说,凡是有一个可以使用的地方,另一个也可以使用.但是,两者之间也还是一个非常重要的区别:在 echo 函数中,可以同时输出多 ...

  10. 【原】push过快的错误 (Pushing the same view controller instance more than once is not supported)

    今天在点击按钮push viewController 时,控制台报错: Terminating app due to uncaught exception 'NSInvalidArgumentExce ...