了解 Qt 多线程编程 新手必学

移动开发
Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功能。为了满足用户构造复杂图形界面系统的需求,Qt 提供了丰富的多线程编程支持。

本文介绍的是了解 Qt 多线程编程,本文并没有过多的去总结多线程的概念,通过了解线程类和一些线程的实现,去真正的弄清楚。首先,我们先来 看文章的讲解。

概述

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

Qt 线程类

Qt 包含下面一些线程相关的类:

QThread 提供了开始一个新线程的方法

QThreadStorage 提供逐线程数据存储

QMutex 提供相互排斥的锁,或互斥量

QMutexLocker 是一个便利类,它可以自动对QMutex加锁与解锁

QReadWriterLock 提供了一个可以同时读操作的锁

QReadLocker与QWriteLocker 是便利类,它自动对QReadWriteLock加锁与解锁

QSemaphore 提供了一个整型信号量,是互斥量的泛化

QWaitCondition 提供了一种方法,使得线程可以在被另外线程唤醒之前一直休眠。

Qt 高级线程类

QtConcurrent 开启线程事务

QFutureWatcher 观测线程状态

QFuture 线程启动类

QThread创建线程

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

  1. class MyThread : public QThread  
  2.  {  
  3.      Q_OBJECT  
  4.  protected:  
  5.      void run();  
  6.  };  
  7.  void MyThread::run()  
  8.  {  
  9.      ...  
  10.  } 

之后调用start,Qt即可创建一个线程,并在线程中执行run()函数中代码,注意UI非线程安全的。

QtConcurrent创建线程

QtConcurrent 创建线程的方法比较多, 而且QtConcurrent 本身比较特殊,若系统有空闲线程时,它会调度空闲线程,无空闲线程时将会创建一个线程。(注意:QtConcurrent 创建线程归QthreadPool管理,若超过***线程数,将会进入队列等待),QtConcurrent创建线程的方法多种,以下举例map函数:

  1. QImage scale(const QImage &image)  
  2.  {  
  3.      qDebug() < < "Scaling image in thread" << QThread::currentThread();  
  4.      return image.scaled(QSize(100, 100), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);  
  5.  }  
  6.  
  7.  int main(int argc, char *argv[])  
  8.  {  
  9.      QApplication app(argc, argv);  
  10.  
  11.      const int imageCount = 20;  
  12.  
  13.      // Create a list containing imageCount images.  
  14.      QList images;  
  15.      for (int i = 0; i < imageCount; ++i)  
  16.          images.append(QImage(1600, 1200, QImage::Format_ARGB32_Premultiplied));  
  17.  
  18.      // Use QtConcurrentBlocking::mapped to apply the scale function to all the  
  19.      // images in the list.  
  20.      QList thumbnails = QtConcurrent::blockingMapped(images, scale);  
  21.  
  22.      return 0;  
  23.  } 

#p#

Qt 线程同步

QMutex, QReadWriteLock, QSemaphore, QWaitCondition 提供了线程同步的手段。使用线程的主要想法是希望它们可以尽可能并发执行,而一些关键点上线程之间需要停止或等待。例如,假如两个线程试图同时访问同一个 全局变量,结果可能不如所愿。

QMutex

QMutex 提供相互排斥的锁,或互斥量。在一个时刻至多一个线程拥有mutex,假如一个线程试图访问已经被锁定的mutex,那么它将休眠,直到拥有mutex的线程对此mutex解锁。Mutexes常用来保护共享数据访问。

QReadWriterLock

QReadWriterLock 与QMutex相似,除了它对 “read”,”write”访问进行区别对待。它使得多个读者可以共时访问数据。使用QReadWriteLock而不是QMutex,可以使得多线程程序更具有并发性。

  1. QReadWriteLock lock;  
  2.  void ReaderThread::run()  
  3.  {  
  4.      lock.lockForRead();  
  5.      read_file();  
  6.      lock.unlock();  
  7.  }  
  8.  void WriterThread::run()  
  9.  {  
  10.      lock.lockForWrite();  
  11.      write_file();  
  12.      lock.unlock();  
  13.  } 

QSemaphore

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

  1. const int DataSize = 100000;  
  2.  const int BufferSize = 8192;  
  3.  char buffer[BufferSize];  
  4.  QSemaphore freeBytes(BufferSize);  
  5.  QSemaphore usedBytes;  
  6.  class Producer : public QThread  
  7.  {  
  8.  public:  
  9.      void run();  
  10.  };  
  11.  
  12.  void Producer::run()  
  13.  {  
  14.      qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));  
  15.      for (int i = 0; i < DataSize; ++i) {  
  16.          freeBytes.acquire();  
  17.          buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];  
  18.          usedBytes.release();  
  19.      }  
  20.  }  
  21.  class Consumer : public QThread  
  22.  {  
  23.  public:  
  24.      void run();  
  25.  };  
  26.  void Consumer::run()  
  27.  {  
  28.      for (int i = 0; i < DataSize; ++i) {  
  29.          usedBytes.acquire();  
  30.          fprintf(stderr, "%c", buffer[i % BufferSize]);  
  31.          freeBytes.release();  
  32.      }  
  33.      fprintf(stderr, "\n");  
  34.  }  
  35.  int main(int argc, char *argv[])  
  36.  {  
  37.      QCoreApplication app(argc, argv);  
  38.      Producer producer;  
  39.      Consumer consumer;  
  40.      producer.start();  
  41.      consumer.start();  
  42.      producer.wait();  
  43.      consumer.wait();  
  44.      return 0;  
  45.  }  

#p#

QWaitCondition

QWaitCondition 允许线程在某些情况发生时唤醒另外的线程。一个或多个线程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()设置一个条件。wakeOne()随机唤醒一个,wakeAll()唤醒所有。

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

  1. const int DataSize = 100000;  
  2.  const int BufferSize = 8192;  
  3.  char buffer[BufferSize];  
  4.  QWaitCondition bufferNotEmpty;  
  5.  QWaitCondition bufferNotFull;  
  6.  QMutex mutex;  
  7.  int numUsedBytes = 0;  
  8.  
  9.  class Producer : public QThread  
  10.  {  
  11.  public:  
  12.      void run();  
  13.  };  
  14.  void Producer::run()  
  15.  {  
  16.      qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));  
  17.  
  18.      for (int i = 0; i < DataSize; ++i) {  
  19.          mutex.lock();  
  20.          if (numUsedBytes == BufferSize)  
  21.              bufferNotFull.wait(&mutex);  
  22.          mutex.unlock();  
  23.  
  24.          buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];  
  25.  
  26.          mutex.lock();  
  27.          ++numUsedBytes;  
  28.          bufferNotEmpty.wakeAll();  
  29.          mutex.unlock();  
  30.      }  
  31.  }  
  32.  
  33.  class Consumer : public QThread  
  34.  {  
  35.  public:  
  36.      void run();  
  37.  };  
  38.  void Consumer::run()  
  39.  {  
  40.      for (int i = 0; i < DataSize; ++i) {  
  41.          mutex.lock();  
  42.          if (numUsedBytes == 0)  
  43.              bufferNotEmpty.wait(&mutex);  
  44.          mutex.unlock();  
  45.  
  46.          fprintf(stderr, "%c", buffer[i % BufferSize]);  
  47.  
  48.          mutex.lock();  
  49.          --numUsedBytes;  
  50.          bufferNotFull.wakeAll();  
  51.          mutex.unlock();  
  52.      }  
  53.      fprintf(stderr, "\n");  
  54.  }  
  55.  int main(int argc, char *argv[])  
  56.  {  
  57.      QCoreApplication app(argc, argv);  
  58.      Producer producer;  
  59.      Consumer consumer;  
  60.      producer.start();  
  61.      consumer.start();  
  62.      producer.wait();  
  63.      consumer.wait();  
  64.      return 0;  
  65.  } 

小结:对于Qt 中多线程编程的介绍就到这了,希望本篇文章对你有所帮助。

责任编辑:zhaolei 来源: 互联网
相关推荐

2011-06-16 11:04:07

Qt

2011-06-16 17:19:33

Qt Meego

2011-06-13 10:03:19

Qt 多线程 编程

2011-06-16 11:28:48

Qt QApplicati

2011-06-16 11:13:13

QtQWidget

2011-06-20 13:43:08

Qt Socket 线程

2011-06-22 16:18:23

QT 多线程 QSocket

2013-07-16 10:12:14

iOS多线程多线程概念多线程入门

2009-03-12 10:52:43

Java线程多线程

2023-06-13 13:39:00

多线程异步编程

2011-06-22 10:12:08

Qt 线程

2023-06-07 13:49:00

多线程编程C#

2023-06-06 08:17:52

多线程编程Thread类

2023-06-05 07:56:10

线程分配处理器

2023-04-02 17:53:10

多线程编程自测

2011-06-22 16:08:40

Qt 多线程 事件循环

2011-06-30 17:31:32

Qt 多线程 信号

2013-07-16 12:13:27

iOS多线程多线程概念GCD

2011-06-07 17:35:39

iphone 多线程

2018-10-25 15:55:44

Java多线程锁优化
点赞
收藏

51CTO技术栈公众号