【发布时间】:2021-01-15 08:53:26
【问题描述】:
我已经阅读了许多 stackoverflow 答案和一些博客文章(也包括 QThread 文档),但我仍然不确定如何在 QThreads 仍在运行时处理退出的主应用程序(同时不会导致应用程序崩溃)。使这变得困难的事实是,这些 QThread 实例包含不同类型的阻塞函数,例如 curl 查询、c++ 文件 io 等。这使得使用 isInterruption 请求的 API 变得很困难(我会在理想情况下使用它)。最重要的是,我必须确保这些在 Linux、Windows 和 OSX 上都能正常工作。我们可以在这里假设任何文件 io 损坏或不完整的网络查询都不会让我们担心并得到照顾。这是整个工作示例zipped。我以两种不同的方式使用 Qthreads,我将在这里进行演示,总体思路是将应用程序的 aboutToQuit() 与线程的 terminate() 绑定,假设这是安全的,因为无论如何应用程序都会关闭 -
-
工人/控制器
class Worker : public QObject { Q_OBJECT public slots: void doWork(const QString &msg) { QThread::sleep(10); emit resultReady("Finished"); } // simulates thread doing work signals: void resultReady(const QString &result); }; class Controller : public QObject { Q_OBJECT QThread workerThread; public: Controller() { Worker *worker = new Worker; worker->moveToThread(&workerThread); connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); connect(this, &Controller::operate, worker, &Worker::doWork); connect(worker, &Worker::resultReady, this, &Controller::handleResults); connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(terminateThread())); workerThread.start(); } ~Controller() { workerThread.quit(); workerThread.wait(); } public slots: void handleResults(const QString &msg); void terminateThread() { workerThread.terminate(); workerThread.wait(); } signals: void operate(const QString &msg); }; // can be started like Controller c; emit c.operate("Starting Thread");
-
方法 run() 覆盖
class WorkerThread : public QThread { Q_OBJECT void run() override { QThread::sleep(10); emit resultReady("Finished");} signals: void resultReady(const QString &s); public slots: void terminateThread() { workerThread.terminate(); workerThread.wait(); } }; class MyObject : public QObject { Q_OBJECT public slots: void handleResults(const QString &msg); public: void startWorkInAThread() { WorkerThread *workerThread = new WorkerThread(); connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults); connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater); connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), workerThread, SLOT(terminateThread())); workerThread->start(); } }; // can be started like MyObject obj; obj.startWorkInAThread();
我使用 main() 测试这两种方法,看起来像 -
QCoreApplication a(argc, argv);
std::puts("Starting app");
// Controller c;
// emit c.operate("Starting Thread");
MyObject obj;
obj.startWorkInAThread();
QTimer::singleShot(2000, [&]{
std::puts("Quit pressed");
a.quit();
});
return a.exec();
现在的问题 -
-
如果我使用 Controller 方法,Linux 会给出一些关于退出时未捕获异常的错误消息。如果我删除 .wait() 调用,该消息就会消失,但那些应该是必要的。 Windows 退出正常,但返回码是一些负数。 OSX 使应用程序崩溃,可能是由于 .wait() 调用。
Qt has caught an exception thrown from an event handler. Throwing exceptions from an event handler is not supported in Qt. You must not let any exception whatsoever propagate through Qt code. If that is not possible, in Qt 5 you must at least reimplement QCoreApplication::notify() and catch all exceptions there. (process:3416): GLib-CRITICAL **: 22:45:51.656: g_source_unref_internal: assertion 'source != NULL' failed -
如果我使用 run() 覆盖,Linux 工作得非常好,OSX 也是如此。在 Windows 上,程序永远不会完成,卡在 .terminate() 调用上,或者如果我删除它,然后在 .wait() 调用上。
-
还有一些用法(在实际应用中,不在给定示例中)使用 run() 覆盖但 run 方法的所有者类被初始化为 -
WorkerThread *workerThread = new WorkerThread(this);并且由于this被设置为它的父类,它以某种方式在退出时使程序崩溃。 -
我实际上不想等待线程并希望它们在我的程序退出时尽快退出,一些阻塞网络操作可能需要几分钟才能完成。
我想我已经详细解释了所有内容,但总而言之,我希望我的应用程序在运行后台线程的情况下优雅地退出,并且根本不会崩溃。并在所有受支持(提及)的 QT 平台上执行此操作。
【问题讨论】:
-
如果你想要一些通用的方法来停止线程&不等待&不崩溃&不做任何正确的停止阻塞操作&同时在 - 这是不可能的
标签: c++ linux multithreading qt c++11