繁体   English   中英

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

[英]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.doWork = false

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

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

它在很大程度上取决于你如何使用线程。

如果您将它们用作单独的eventloops,而不是“工作线程”,只需通过退出QCoreApplication::aboutToQuit信号中的线程来停止:

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

(对于多个线程,首先退出所有线程然后等待)

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

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

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

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

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

但不建议依赖它,并且当执行可能具有“持久”的副作用时,例如写入文件或与其他进程(包括共享内存)通信可能在终止后仍然存在,可能无法轻松清理。 文件可以保持半写,其他进程可以接收半完整消息或向未声明终止的进程发送消息。

在突然结束的进程中识别内存泄漏也很困难。

最佳实践总是会使所有线程得出已知的结论。

推荐的终止方法是定义一个或多个stop标志(通常是bool ),线程将在“安全”点检查以终止。 那些stop标志应该是原子的( std::atomic<> ),或者如果在wait()条件下使用,则由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;
}

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

我有从预启动运行的预定义线程,并且在应用程序退出之前不会停止,即它们是永久性的。 它们运行自己的事件循环,并通过信号和插槽与其他线程通信。 这些是正常的线程,而不是基于任务的并发。 我该如何组织这种类型的多线程?

通过将具有预定义信号和槽的对象“移动”到其方法应该运行的线程,在Qt中组织起来非常容易。

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工作线程通过信号和插槽进行通信。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM