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

时间:2019-11-21 21:11:10

标签: c++ multithreading qt

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

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

我有信心在调用析构函数时,所有线程都在一个条件变量上等待(由永远为false的谓词控制的虚假唤醒),并且可连接的输出为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析构函数中的循环永远不会比第一次连接移动得更远-这是我所不期望且无法解释的行为

2 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:1)

  

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

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

  

发现Threadpool析构函数内部的循环从来没有比第一次连接更远

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

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

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

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