Qt之QThread基本用法

简述

多线程与多进程是现代操作系统中非常重要的两个概念,多进程强调分工,多线程强调合作,本文只谈多线程。

多线程有硬件多线程和软件多线程之分,下面只谈软件多线程。

多线程的能力一般是由操作系统(如Windows、Linux)提供,不同的操作系统在多线程的实现上是不同的,而且呈现出来的接口也是不统一的。很多程序开发框架都提供了独立于操作系统平台的多线程接口。

Qt的QThread提供了一个独立于平台的方法管理多线程。一个QThread对象管理着一个线程。QThread通过执行run()开始。默认情况下,run()通过调用exec()开启事件循环。

基本用法

方法1

  class Worker : public QObject
  {
      Q_OBJECT

  public slots:
      void doWork(const QString &parameter) {
          QString result;
          /* ... here is the expensive or blocking operation ... */
          emit resultReady(result);
      }

  signals:
      void resultReady(const QString &result);
  };

  class Controller : public QObject
  {
      Q_OBJECT
      QThread workerThread;
  public:
      Controller() {
          Worker *worker = new Worker;
          worker->moveToThread(&workerThread);
          connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
          connect(this, &Controller::operate, worker, &Worker::doWork);
          connect(worker, &Worker::resultReady, this, &Controller::handleResults);
          workerThread.start();
      }
      ~Controller() {
          workerThread.quit();
          workerThread.wait();
      }
  public slots:
      void handleResults(const QString &);
  signals:
      void operate(const QString &);
  };

上面的程序中有两个类:Worker、Controller。Worker中的doWork()成员函数会做一些耗时的操作。

在Controller中实例化一个Worker。再实例化一个QThread作为Worker的执行线程。通过moveToThread将worker绑定到workerThread。

worker中的槽函数将会在独立的线程workerThread中运行。我们可以将worker的槽函数绑定到来自任何线程的任何对象的任何信号。跨线程的信号槽连接是安全的。

方法2

使用QThread的另一个方法是通过子类化QThread并重写run(),如下所示

  class WorkerThread : public QThread
  {
      Q_OBJECT
      void run() override {
          QString result;
          /* ... here is the expensive or blocking operation ... */
          emit resultReady(result);
      }
  signals:
      void resultReady(const QString &s);
  };

  void MyObject::startWorkInAThread()
  {
      WorkerThread *workerThread = new WorkerThread(this);
      connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
      connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
      workerThread->start();
  }

在上面的例子中,线程将会在run()函数返回后结束。除非在run中调用.exec(),否则将不会有任何的事件循环在该线程中运行。

在方法2中,QThread实例存在于旧的线程中,而不在新的线程中。这意味着QThread的所有槽函数和引用方法都会在旧的线程中被执行。因此,如果开发者想要在新的线程中引用槽函数就必须采用方法1。新的槽函数不应当被直接实例化到QThread的子类中。

当子类化QThread的时候,记住,构造函数在旧的线程中执行,run()在新的线程中执行。如果两个函数都用到了某个变量,那么这个变量就是在被多个线程使用。请确认这样做是安全的。

管理线程

QThread通过信号started()和finished()来提示线程的开始和结束。也可以通过isFinished()和isRunning()来主动请求线程的状态。

可以通过exit()和quit()来停止线程。在极端的情况下,可以通过terminate()来强制结束一个线程。但是,这样做是很危险的,尽量不要这样做。

从Qt 4.8开始,释放一个结束线程中的资源变得非常方便。只需要绑定finished()和QObject::deleteLater()。

通过wait()来阻塞一个线程,直到其它线程结束执行。

QThread也提供了静态的、平台独立的睡眠函数:sleep(),msleep(),和usleep()。

注意:由于Qt是一个事件驱动的框架,尽量用finished(),而不是wait(),尽量用QTimer,而不是sleep()。

静态函数currentThreadID和currentThread返回当前线程的标识符,前者返回平台独立的特定ID,后者返回QThread指针。

setPriority用来设置进程优先级,setStackSize用来设置栈深。

扩展阅读

Qt 多线程编程之敲开 QThread 类的大门

Qt 中的多线程技术

引用

[1] Qt助手

[2] 维基百科–多线程

你可能感兴趣的:(Qt基础)