QThread
基本上,有一个长期存在的 API 错误:它并不总是处于可破坏状态。在 C++ 中,当可以安全地调用其析构函数时,对象被认为处于可破坏状态。破坏跑步QThread
是一个错误。AQThread
只是一个线程控制器,它不是“线程”本身。想想如何QFile
行动:你可以随时破坏它,无论它是否打开。它真正将文件的概念封装为资源。AQThread
对本机(系统)线程的包装太薄:当您破坏它时,它不会终止,也不会处置本机线程(如果有的话)。这是一个资源泄漏(线程是操作系统资源),人们一遍又一遍地被这个问题绊倒。
当应用程序的main()
函数返回时,C/C++ 运行时库的实现恰好终止了应用程序的所有线程,从而有效地终止了整个应用程序。这是否是您想要的行为取决于您。你应该quit()
和wait()
你的事件循环运行线程。对于没有事件循环的线程,quit()
是无操作的,您必须实现自己的退出标志。在破坏它之前,您必须 wait()
在线程上。这是为了防止竞争条件。
下面是QThread
. 这是最后一堂课,因为您无法重新实现run
. 这很重要,因为 run 的重新实现可以以quit
一种无操作的方式完成,从而破坏了 class 的合同。
#include <QThread>
#include <QPointer>
class Thread : public QThread {
using QThread::run; // final
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
};
class ThreadQuitter {
public:
typedef QList<QPointer<Thread>> List;
private:
List m_threads;
Q_DISABLE_COPY(ThreadQuitter)
public:
ThreadQuitter() {}
ThreadQuitter(const List & threads) : m_threads(threads) {}
ThreadQuitter(List && threads) : m_threads(std::move(threads)) {}
ThreadQuitter & operator<<(Thread* thread) {
m_threads << thread; return *this;
}
ThreadQuitter & operator<<(Thread& thread) {
m_threads << &thread; return *this;
}
~ThreadQuitter() {
foreach(Thread* thread, m_threads) thread->quit();
}
};
它可以按如下方式使用:
#include <QCoreApplication>
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
QObject worker1, worker2;
Thread thread1, thread2;
// Style 1
ThreadQuitter quitter;
quitter << thread1 << thread2;
// Style 2
ThreadQuitter quitterB(ThreadQuitter::List() << &thread1 << &thread2);
//
worker1.moveToThread(&thread1);
worker2.moveToThread(&thread2);
thread1.start();
thread2.start();
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
return app.exec();
}
从 中返回时main
,线程退出者将quit()
所有工作线程。这允许线程并行结束。然后,thread2.~Thread
将等待该线程完成,然后thread1.~Thread
将执行相同的操作。线程现在消失了,对象是无线程的并且可以安全地销毁:worker2.~QObject
首先调用,然后是worker1.~QObject
.