我有一个名为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析构函数中的循环永远不会比第一次连接移动得更远-这是我所不期望且无法解释的行为
答案 0 :(得分:6)
join()
对子线程不执行任何操作-直到子线程退出之前,它所做的所有操作都是阻塞的。它仅对调用线程有影响(即通过阻止其进度)。子线程可以保持运行所需的时间(尽管通常情况下,您希望它快速退出,以使调用join()
的线程不会长时间被阻塞-但这取决于您实施)
答案 1 :(得分:1)
令我惊讶的是,加入这些活动线程并不会将它们从top给出的线程列表中删除。这是预期的行为吗?
这表明线程仍在运行。在线程上调用join()
对正在运行的线程没有任何影响。只是调用线程
等待被调用线程退出。
发现Threadpool析构函数内部的循环从来没有比第一次连接更远
这意味着第一个线程尚未完成。因此,其他任何线程都尚未加入(即使它们退出了)。
但是,如果正确实现了线程功能,则第一个线程(以及池中的所有其他线程)应最终完成并
join()
调用应该返回(假设应该退出池中的线程-但这通常不需要如此。
根据应用程序的不同,您可以简单地使线程也永远运行)。
因此,似乎存在某种死锁,或者正在等待占用一个或多个线程的某些资源。因此,您需要运行调试器。 Helgrind将非常有用。
您还可以尝试减少线程数(例如2),并查看问题是否可重现/显而易见,然后可以增加线程数。