等待加入条件变量的线程会发生什么?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了等待加入条件变量的线程会发生什么?相关的知识,希望对你有一定的参考价值。

我有一个名为TThreadpool的类,该类具有std::vector<std::thread>>类型的成员池,并带有以下析构函数:

~TThreadpool() {
    for (size_t i = 0; i < pool.size(); i++) {
        assert(pool[i].joinable());
        pool[i].join();
    }
}

我相信,在调用析构函数时,所有线程都在一个条件变量上等待(使用永远为假的谓词控制的虚假唤醒,并且可连接的输出为true。

简化的运行线程示例为:

void my_thread() {
    std::unique_lock<std::mutex> lg(mutex);
    while (true) {
        my_cond_variable.wait(lg, [] {
            return false;
        });
        # do some work and possibly break, but never comes farther then wait 
        # so this probably should not matter
    }
}

要检查正在运行的线程,我正在启动top -H。在程序启动时,pool.size()本身存在TThreadpool个线程+ 1个线程。令我惊讶的是,加入这些活动线程并不会将它们从top给出的线程列表中删除。这是预期的行为吗?

((最初,我的程序有些不同-我使用qt创建了一个简单的ui应用程序,该应用程序使用在ui线程和其他受线程池控制的线程中运行的线程池,并在关闭ui窗口时调用了线程连接,但是QtCreator说我关闭窗口后我的应用程序仍然可以工作,需要我将其关闭以使其崩溃,这使我检查了线程的状态,结果发现它与qt无关。我用qt错过了一些明显的细节。

稍后,我尝试不声明可连接性,而是打印它,并发现Threadpool析构函数中的循环永远不会比第一次连接移动得更远-我没有想到并且无法解释的行为

答案

join()对子线程不做任何事情-直到子线程退出之前,它所做的一切都是阻塞的。它仅对调用线程有影响(即通过阻止其进度)。子线程可以保持运行所需的时间(尽管通常您希望它快速退出,这样调用join()的线程不会长时间被阻塞-但这取决于您实现)

另一答案

令我惊讶的是,加入这些活动线程并不会将它们从top给出的线程列表中删除。这是预期的行为吗?

这表明线程仍在运行。在线程上调用join()对正在运行的线程没有任何影响。只是调用线程等待被调用线程退出。

发现Threadpool析构函数内部的循环从未比第一次连接更深入

这意味着第一个线程尚未完成。因此,其他所有线程都尚未加入(即使它们退出了)。

但是,如果线程功能正确实现,则第一个线程(以及池中的所有其他线程)应最终完成,join()调用应该返回(假设应该退出池中的线程-但这通常不需要为true。根据应用程序,您可以简单地使线程永远运行)。

因此,似乎出现了某种死锁,或者正在等待占用一个或多个线程的某些资源。因此,您需要运行调试器。Helgrind非常有用。

您还可以尝试减少线程数(例如2),并查看问题是否可重现/明显,然后可以增加线程数。

以上是关于等待加入条件变量的线程会发生什么?的主要内容,如果未能解决你的问题,请参考以下文章

四十Linux 线程——线程同步之条件变量

主程序如何通过等待而不是加入同时等待多个线程?

线程同步——条件变量

线程同步——条件变量

互斥锁自旋锁读写锁和条件变量

条件变量