如何优雅地退出多线程应用程序?

How to gracefully quit a multi-threaded application?

如果我有一个 Qt 应用程序(使用 QCoreApplication),并且该应用程序启动了一些永久线程,关闭该应用程序的正确方法是什么?

在一个线程中只 运行 QCoreApplication::quit() 可以吗?这会导致其他线程正常终止(并调用所有包含的对象的析构函数,而不是被强制终止)吗?

更多细节 解释线程的性质:它们是预定义的并且 运行 从启动开始并且在应用程序退出之前不会停止,即它们是永久的.他们 运行 自己的事件循环并通过信号和槽与其他线程通信。这些是普通线程,不是基于任务的并发。

最长的 运行 'thread main' 函数的形式有点像下面这样:

while (doWork) {
    work();
}

doWork 是 std::atomic<bool>

当主线程想要退出时,它会在所有仍然存在的线程上设置 myThread.doWork = false,这允许它们在准备就绪时退出。

通过在主线程上调用 myThread.wait(),它会阻塞,直到您告诉它停止工作的线程实际停止为止。 在为所有线程执行此操作时,当主线程离开 main() 时,它仍然是唯一的线程 运行.

旁注: 如果你必须等待工作被推送到它,你可能想要查看 QWaitCondition class 这样你就可以在有工作和你想要它停止时唤醒你的线程。

我同意@UKMonkey 关于线程块的想法,但如果某些线程正在等待设备或内存并且条件等待,则不能保证线程退出,甚至会阻止应用程序退出。 那么对于这些​​情况该怎么办,QCoreApplication 有 aboutToQuit() 信号,你可以将它连接到一个插槽并强制你的线程退出并检查线程是否没有正常退出并更正它是退出场景。

这在很大程度上取决于您如何使用线程。

如果您将它们用作单独的事件循环,而不是 "worker threads",只需从 QCoreApplication::aboutToQuit 信号中退出线程即可停止:

QObject::connect(qApp, &QCoreApplication::aboutToQuit, thread, [thread](){
    thread->quit();
    thread->wait(1000);
});

(多线程先全部退出再等待)

如果您将它们用作真正的工作线程,在循环中进行永久工作等,您可以使用 QThreads 中断机制。在您的线程中执行:

while(!QThread::currentThread()->isInterruptionRequested()) {
    // code...
}

并以非常相似的方式退出它们:

QObject::connect(qApp, &QCoreApplication::aboutToQuit, thread, [thread](){
    thread->requestInterruption();
    thread->wait(1000);
});

大多数现代平台的目标是 'clean up' 在进程突然结束后。 那就是结束所有线程,恢复所有内存,关闭所有打开的文件并恢复分配给进程的任何其他资源或句柄。

但不建议依赖它,当执行可能有 'durable' 副作用时,例如写入文件或与可能在终止后仍然存在的其他进程(包括共享内存)通信,它可能无法执行轻松清理。文件可能保持一半写入状态,其他进程可能会收到一半完成的消息或将消息发送到未声明终止的进程。

识别突然终止的进程中的内存泄漏也非常困难。

最佳做法始终是让所有线程得出一个已知结论。

推荐的终止方式是定义一个或多个 stop 标志(通常是 bool),线程将在 'safe' 点检查这些标志以终止。 如果在 wait() 条件下使用,那些 stop 标志应该是原子的 (std::atomic<>) 或受 std::mutex 保护。

在该模型中,终止代码可能类似于..

#include <iostream>
#include <atomic>
#include <mutex>
#include <thread>
#include <vector>

std::atomic<bool> stop_flag;
std::vector<std::thread> threads;

std::mutex cout_mutex;//std::cout is not natively synchronized.

void chug(size_t index){
    int i{0};
    while(!stop_flag){
        {
            std::lock_guard<std::mutex> guard{cout_mutex};
            std::cout<<index<<" : "<<i<<std::endl;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));//slow it down!
        ++i;
    }
}


//stop_all brings all the threads to a safe and known conclusion.
void stop_all(){
    stop_flag=true;
    for( auto& curr: threads){
        curr.join();
    }
}


int main() {
    const size_t num{10};
    for(size_t i=0;i<num;++i){
        threads.emplace_back(chug,i);
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));//Let it run!

    stop_all();

    return 0;
}

通过问题解释的作者,我们可以缩小到线程的确切类型:

I have predefined threads that run from startup and do not stop until the application exits, i.e. they're permanent. They run their own event loop and communicate with other threads via signals and slots. These are normal threading, not task-based concurrency. How can I organize this type of multi-threading?

在 Qt 中很容易通过 "moving" 将具有预定义信号和槽的对象组织到其方法应该 运行 在其中的线程。

class Worker : public QObject
{
    Q_OBJECT
public:
    Worker(Load*);

signals:
    void produce(Result*);

public slots:
    void doWork(Load*);
};

void Worker::doWork(Load* pLoad)
{
    // here we can check if thread interruption is requested
    // if the work is of iterative long time type
    while(!QThread::currentThread()->isInterruptionRequested())
    {
        process(pLoad);
        if (pLoad->finished())
        {
           emit produce(pLoad->result()); // deliver the result
           return; // back to worker thread event loop to wait
        }
    }
    // interrupted before the load finished
    QThread::currentThread()->quit();
}


// { somewhere on the main thread
// main thread launches worker threads like this one
QThread thread;
Worker worker(new Load());
worker.moveToThread(&thread); // the worker will be leaving in the thread
// start!
thread.start();

// worker thread adds new workload on its thread
// through the thread event loop by invokeMethod
QMetaObject::invokeMethod(&worker, "doWork", Qt::AutoConnection,
                          Q_ARG(Load*, new Load));

// after all we want to quit the app
thread.requestInterruption(); // for faster finishing
// or
thread.quit(); // for finishing after the last data processed
thread.wait(); // for one thread wait but you can catch finished() 
      // signals from many threads (say, count until all signaled)

// ........
// quit the app
// ........
qApp->quit();
// } somewhere on main thread

它可能看起来有点像基于任务的并发,但线程上没有任务对象从队列中获取负载。它只是演示了 Qt 工作线程通过信号和槽进行通信。