有两个类:Widget
和Worker
。这是一个示意图代码。
class Widget
{
Worker *m_worker;
QTextEdit *m_edit;
public:
Widget():m_edit(new QTextEdit){}
~Widget()
{
m_worker->ShouldStop = true;
delete *m_worker;
}
void doWork()
{
m_worker = new Worker;
if (!worker->doWork())
m_edit->setText("failed");
}
}
class Worker
{
Worker() : ShouldStop(false){}
public:
bool ShouldStop;
bool doWork()
{
while(true && !ShouldStop)
{
QThread::sleep(1);
QApplication::processEvents();
}
//consider the work undone if a stop was forced
if (ShouldStop)
return false;
}
}
在doWork()
的方法Widget
中调用doWork()
Worker
执行循环后。然后关闭窗口小部件,并在对processEvents()
的一次调用期间调用其析构函数。然后执行返回doWork()
的{{1}}。
它现在会检查Worker
并返回ShouldStop
的{{1}}并尝试向doWork()
添加内容。但是Widget
对象已经死了。
问题:
答案 0 :(得分:3)
理想情况下,工作线程应该只通过信号和插槽机制返回数据,而不是直接访问原始对象。实际上创建一个线程并避免调用QApplication::processEvents()
是避免这个问题的第一种方法。
此外,当您想要在新线程中启动worker时,应考虑使用这样的设计。您可以创建一个通用QThread并为其分配QObject,而不是子类化QThread,如下所示:
Worker *worker = new Worker;
QThread *workerThread = new QThread(this);
connect(workerThread, &QThread::started, worker, &Worker::doWork);
connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);
worker->moveToThread(workerThread);
// Starts an event loop, and emits workerThread->started()
workerThread->start();
如果您使用Qt 5,请考虑围绕此模式重构代码。
答案 1 :(得分:2)
@Alex答案绝对是q2的方法。他只是忘了提到如何进行删除(q1)。假设Widget
创建了对象workerThread
,那么这是必要的:
~Widget(){
if(workerThread->isRunning()){
workerThread->terminate(); <--- this line, right there
workerThread->wait():
}
...
}
为什么呢?因为删除QThread
只是删除线程对象but doesn't stop the thread from running, and may crash the application。请注意,如果您正在使用
connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);
worker
之后访问terminate
是不安全的,因为它会触发finished()
信号,执行worker->deleteLater
,最终删除worker
。
答案 2 :(得分:1)
你可以让Widget使用一个信号来通知Widget的所有者为Widget启动Worker。
所有者拥有Widget和Worker。 Widget告诉所有者触发了某些事情,并且所有者负责决定Worker的doWork()是正确的响应,以及管理Worker和Widget的生命周期。工人和小工具彼此无知,但所有者可能会将他们的一些信号和插槽连接在一起。