sth*_*m58 6 c++ qt multithreading worker qthread
在下面的示例中(在Qt GUI应用程序中)启动了一个新线程(带有一个事件循环,我希望在其中完成一些工作):
void doWork()
{
QThread* workerThread = new QThread();
Worker* worker = new Worker();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(started()), worker, SLOT(startWork()));
connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
Run Code Online (Sandbox Code Playgroud)
startWork()
可以是一个长时间运行的操作,在此期间可以关闭应用程序.
我希望只要在应用程序startWork()
上执行应用程序就不会关闭workerThread
.但是,当我关闭最后一个应用程序窗口时,它workerThread
会立即消失(在长时间运行期间)并且应用程序关闭没有问题.
问题出现了:
workerThread
擦拭?
wait()
QThread(最终)?
wait()
进入一个插槽,aboutToQuit()
并且在长时间运行操作完成后应用程序没有关闭(如上所述设置).仅quit(); wait();
(在提到的插槽内)允许应用程序关闭.为什么?QThread
基本上,它有一个长期存在的API错误:它并不总是处于可破坏状态.在C++中,当一个对象可以安全地调用它的析构函数时,它被认为是可破坏的状态.破坏运行QThread
是一个错误.A QThread
只是一个线程控制器,它不是"线程"本身.想想QFile
行为:你可以随时破坏它,无论它是否开放.它真正将文件的概念封装为资源.A QThread
本机(系统)线程周围的包装太薄了:当你破坏它时,它不会终止也不会处理本机线程(如果有的话).这是资源泄漏(线程是OS资源),人们一遍又一遍地绊倒这个问题.
当应用程序的main()
函数返回时,C/C++运行时库的实现会终止所有应用程序的线程,从而有效地终止整个应用程序.这是否是您想要的行为取决于您.你应该quit()
和wait()
你的事件循环运行线程.对于没有事件循环的线程,quit()
是一个无操作,你必须实现自己的退出标志.你必须 wait()
在线程上你破坏它.这是为了防止竞争条件.
下面是一个安全的包装QThread
.这是最后一堂课,因为你无法重新实现run
.这很重要,因为可以通过这样的方式重新实现运行,从而使得quit
无操作,破坏了类的合同.
#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();
}
};
Run Code Online (Sandbox Code Playgroud)
它可以使用如下:
#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();
}
Run Code Online (Sandbox Code Playgroud)
从返回时main
,线程quitter将quit()
所有工作线程.这允许螺纹平行地向下卷绕.然后,thread2.~Thread
将等待该线程完成,然后thread1.~Thread
将执行相同的操作.线程现在消失了,对象是无线的,可以安全地被破坏:worker2.~QObject
先调用,然后调用worker1.~QObject
.