gpt4 book ai didi

c++ - 当应用程序在没有适当的 wait() 调用的情况下关闭时 QThread 会发生什么?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:27:40 25 4
gpt4 key购买 nike

在下面的示例中(在 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();
}

startWork() 可以是一个长时间运行的操作,在此期间可以关闭应用程序。

我预计只要 startWork()workerThread 上执行,应用程序就不会关闭。看起来,当我关闭最后一个应用程序窗口时,workerThread 立即消失(在长时间运行的操作期间)并且应用程序关闭没有问题。

产生的问题:

  1. 为什么立即删除 workerThread
    • 是 parent / child 的问题吗?
    • Qt 如何处理这种情况?
  2. 程序员是否犯了错误,没有在 QThread 上调用 wait()(最终)?
    • 即使是这样,我还是尝试在 aboutToQuit() 的插槽内 wait() 并且在长时间运行的操作完成后应用程序没有关闭(设置为多于)。只有 quit(); wait(); (在提到的插槽内)允许应用程序关闭。为什么?

最佳答案

QThread 基本上有一个长期存在的 API 错误:它并不总是处于可破坏状态。在 C++ 中,当可以安全调用其析构函数时,对象被认为处于可破坏状态。破坏正在运行的 QThread 是错误的。 QThread 仅仅是一个线程 Controller ,它不是“线程”本身。想一想 QFile 的行为方式:您可以随时销毁它,无论它是否打开。它真正将文件的概念封装为资源。 QThread 是围绕 native (系统)线程的包装器太薄了:当您破坏它时,它不会终止,也不会处置 native 线程(如果有的话)。这是一个资源泄漏(线程是操作系统资源),人们一遍又一遍地被这个问题绊倒。

当应用程序的 main() 函数返回时,您的 C/C++ 运行时库的实现恰好终止了应用程序的所有线程,从而有效地终止了整个应用程序。这是否是您想要的行为取决于您。您应该quit()wait() 您的事件循环运行线程。对于没有事件循环的线程,quit() 是空操作,您必须实现自己的退出标志。在销毁线程之前,您必须在线程上wait()。这是为了防止竞争条件。

下面是 QThread 的安全包装器。这是一个最终类,因为您不能重新实现 run。这很重要,因为可以通过使 quit 成为空操作的方式来完成 run 的重新实现,从而打破 contract of the 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

关于c++ - 当应用程序在没有适当的 wait() 调用的情况下关闭时 QThread 会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19656442/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com