QT的多线程程序设计

2019-03-23 14:16

QT 多线程程序设计

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相似,除了它对 \访问进行区别对待。它使得多个读者可以共时访问数据。使用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] = \[(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, \, buffer[i % BufferSize]); freeBytes.release(); }

fprintf(stderr, \); }

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] = \[(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, \, buffer[i % BufferSize]); mutex.lock();

--numUsedBytes;

bufferNotFull.wakeAll(); mutex.unlock(); }

fprintf(stderr, \); }

int main(int argc, char *argv[]) {

QCoreApplication app(argc, argv); Producer producer; Consumer consumer; producer.start(); consumer.start(); producer.wait(); consumer.wait(); return 0; }

可重入与线程安全

在Qt文档中,术语“可重入”与“线程安全”被用来说明一个函数如何用于多线程程序。假如一个类的任何函数在此类的多个不同的实例上,可以被多个线程同时调用,那么这个类被称为是“可重入”的。假如不同的线程作用在同一个实例上仍可以正常工作,那么称之为“线程安全”的。

大多数c++类天生就是可重入的,因为它们典型地仅仅引用成员数据。任何线程可以在类的一个实例上调用这样的成员函数,只要没有别的线程在同一个实例上调用这个成员函数。举例来讲,下面的Counter 类是可重入的:

class Counter {

public:

Counter() {n=0;}

void increment() {++n;} void decrement() {--n;}

int value() const {return n;} private:

int n; };

这个类不是线程安全的,因为假如多个线程都试图修改数据成员 n,结果未定义。这是因为c++中的++和--操作符不是原子操作。实际上,它们会被扩展为三个机器指令: 1,把变量值装入寄存器 2,增加或减少寄存器中的值


QT的多线程程序设计.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:我明白了为什么统计局热衷于GDP而不是GNP!

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: