一、QThread

https://doc.qt.io/qt-5/qthread.html https://doc.qt.io/qt-5/threads-synchronizing.html KDAB:https://www.youtube.com/watch?v=dcSsjxhazu0

QThread对象在程序中管理一个控制线程。QThreads在run()中开始执行。默认情况下,run()通过调用exec()启动事件循环,并在线程中运行Qt事件循环。

可以通过使用QObject::moveToThread()将worker对象移动到线程中来使用它们。

  1. class Worker : public QObject
  2. {
  3. Q_OBJECT
  4. public slots:
  5. void doWork(const QString &parameter) {
  6. QString result;
  7. /* ... here is the expensive or blocking operation ... */
  8. emit resultReady(result);
  9. }
  10. signals:
  11. void resultReady(const QString &result);
  12. };
  13. class Controller : public QObject
  14. {
  15. Q_OBJECT
  16. QThread workerThread;
  17. public:
  18. Controller() {
  19. Worker *worker = new Worker;
  20. worker->moveToThread(&workerThread);
  21. connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
  22. connect(this, &Controller::operate, worker, &Worker::doWork);
  23. connect(worker, &Worker::resultReady, this, &Controller::handleResults);
  24. workerThread.start();
  25. }
  26. ~Controller() {
  27. workerThread.quit();
  28. workerThread.wait();
  29. }
  30. public slots:
  31. void handleResults(const QString &);
  32. signals:
  33. void operate(const QString &);
  34. };

Worker槽内的代码将在一个单独的线程中执行。但是,你可以自由地将Worker的插槽连接到任何线程中的任何对象的任何信号。由于一种称为排队连接的机制,跨不同线程连接信号和插槽是安全的。

  1. class WorkerThread : public QThread
  2. {
  3. Q_OBJECT
  4. void run() override {
  5. QString result;
  6. /* ... here is the expensive or blocking operation ... */
  7. emit resultReady(result);
  8. }
  9. signals:
  10. void resultReady(const QString &s);
  11. };
  12. void MyObject::startWorkInAThread()
  13. {
  14. WorkerThread *workerThread = new WorkerThread(this);
  15. connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
  16. connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
  17. workerThread->start();
  18. }

在这个例子中,线程将在run函数返回后退出。除非调用exec(),否则线程中不会运行任何事件循环。

重要的是要记住,一个QThread实例存在于实例化它的旧线程中,而不是在调用run()的新线程中。这意味着QThread的所有排队槽和调用的方法都将在旧线程中执行。因此,希望在新线程中调用插槽的开发人员必须使用worker-object方法;新插槽不应该直接实现到QThread子类中。

与排队槽或调用方法不同,直接在QThread对象上调用的方法将在调用该方法的线程中执行。当子类化QThread时,请记住构造函数在旧线程中执行,而run()在新线程中执行。如果一个成员变量从两个函数访问,则该变量从两个不同的线程访问。检查这样做是否安全。

注意:当与不同线程的对象交互时必须小心。一般来说,函数只能从创建QThread对象本身的线程调用(例如setPriority()),除非文档另有说明。请参见同步线程。

使代码在单独的线程中运行的另一种方法是将QThread子类化并重新实现run()。 例如:

  1. class WorkerThread : public QThread
  2. {
  3. Q_OBJECT
  4. void run() override {
  5. QString result;
  6. /* ... here is the expensive or blocking operation ... */
  7. emit resultReady(result);
  8. }
  9. signals:
  10. void resultReady(const QString &s);
  11. };
  12. void MyObject::startWorkInAThread()
  13. {
  14. WorkerThread *workerThread = new WorkerThread(this);
  15. connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
  16. connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
  17. workerThread->start();
  18. }

在这个例子中,线程将在run函数返回后退出。除非调用exec(),否则线程中不会运行任何事件循环。

重要的是要记住,一个QThread实例存在于实例化它的旧线程中,而不是在调用run()的新线程中。这意味着QThread的所有排队槽和调用的方法都将在旧线程中执行。因此,希望在新线程中调用插槽的开发人员必须使用worker-object方法;新插槽不应该直接实现到子类QThread中。

与排队槽或调用方法不同,直接在QThread对象上调用的方法将在调用该方法的线程中执行。当子类化QThread时,请记住构造函数在旧线程中执行,而run()在新线程中执行。如果一个成员变量从两个函数访问,则该变量从两个不同的线程访问。检查这样做是否安全。

注意:与跨不同线程的对象进行交互时必须小心。 通常,除非文档另有说明,否则只能从创建QThread对象本身的线程(例如setPriority())中调用函数。

管理线程


当线程的start()和finish()时,QThread会通过信号通知您,或者您可以使用isFinished()和isRunning()来查询线程的状态。

您可以通过调用exit()或quit()来停止线程。 在极端情况下,您可能需要强制终止执行线程。 但是,这样做是危险的,不鼓励这样做。 请阅读terminate()和setTerminationEnabled()的文档以获取详细信息。

从Qt 4.8开始,可以通过将finish()信号连接到QObject::deleteLater()释放生活在刚刚结束的线程中的对象。

使用wait()阻塞调用线程,直到另一个线程完成执行(或直到经过指定的时间)为止。

QThread还提供了与平台无关的静态睡眠功能:sleep(),msleep()和usleep()分别允许完整的秒,毫秒和微秒的分辨率。 这些功能已在Qt 5.0中公开。

注意:一般来说,因为Qt是一个事件驱动的框架,所以通常不需要使用wait()和sleep()函数。 可以考虑侦听finish()信号,而不是wait()。 考虑使用QTimer,而不是sleep()函数。

静态函数currentThreadId()和currentThread()返回当前正在执行的线程的标识符。 前者返回线程的特定于平台的ID。 后者返回一个QThread指针。

要选择将给您的线程提供的名称(例如,由Linux上的ps -L命令标识),您可以在启动线程之前调用setObjectName()。如果你不调用setObjectName(),给你的线程的名字将是你的线程对象的运行时类型的类名(例如,“RenderThread”在Mandelbrot例子的情况下,因为这是QThread子类的名字)。请注意,这目前在Windows上的发布版本中是不可用的。

二、QMutex

三、QSemaphore

四、QWaitCondition

五、singleton模式

六、QThreadPool