这是一个很好的工作多线程代码。它使用std::async
: -
class C{ public: int d=35; };
class B{
public: C* c;
public: void test(){
std::vector<std::future<void>> cac;
for(int n=0;n<5;n++){
cac.push_back(
std::async(std::launch::async,[&](){
test2();
})
);
}
for(auto& ele : cac){
ele.get();
}
};
public: void test2(){
std::vector<std::future<void>> cac;
for(int n=0;n<5;n++){
cac.push_back(
std::async(std::launch::async,[&](){
int accu=0;
for(int i=0;i<10000;i++){
accu+=i;
}
std::cout<<accu<<" access c="<<c->d<<std::endl;
})
);
}
for(auto& ele : cac){
ele.get();
}
}
};
以下是测试用例: -
int main(){
C c;
B b; b.c=&c;
b.test();
std::cout<<"end"<<std::endl;
}
它有效,但如果我从std::async
更改为使用线程池库,例如
Progschj的ThreadPool (https://github.com/progschj/ThreadPool/blob/master/ThreadPool.h)
ThreadPool pool(4);
...
pool.enqueue([&](){
test2();
})
vit-vit的ctpl (https://github.com/vit-vit/CTPL/blob/master/ctpl_stl.h)
ctpl::thread_pool pool(4);
...
pool.push([&](int threadId){
test2();
})
......我会遇到访问冲突或冻结(可能是死锁)。
这是否意味着我无法从其他任务创建任务? 哪部分代码是限制的原因?如何克服它?
以下是3 MCVE std::async (Coliru),Progschj's ThreadPool (pastebin),ctpl (pastebin)。
我试图深入他们的图书馆,但凭借我有限的经验,我无法找到原因。
在实际情况中,当任务量> 1时,错误趋于发生。螺纹量(4)。
有时,它会导致无关库的线程永远停止。 (例如SDL键盘监听器。)
在更复杂的程序中,Visual Studio有时会捕获this (B*) = 0x02
(我想在a-lambda-with-capture在循环中使用一次后会删除this
的引用;超出范围??)
以下是 ThreadPool 中最可疑的位置(两个库非常相似): -
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
// don't allow enqueueing after stopping the pool
if(stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace([task](){ (*task)(); });
}
condition.notify_one();
return res;
}
答案 0 :(得分:2)
死锁是因为ThreadPool中的任务无法被抢占。因此,如果您以递归方式调用ThreadPool::enqueue
方法,然后等待结果,则会导致死锁,因为所有线程都已使用,无法执行新排队的任务。
更详细一点:
让我们一步一步地完成你的代码
1.你调用B::test()
这个函数在线程池中包含5个任务,然后在ele.get()
中等待他们的结果,即完成它们。
2.线程池中的线程deque其中一个任务(在步骤1中引用),这意味着如果线程数量<=任务量,则所有线程都执行B::test2()
。在这里,您再次在线程池中排队5个新任务
现在到了关键点。稍后在B::test2()
中,您等待ele.get()
这些任务的结果,这意味着线程池的线程被阻塞,直到执行了任务(带有for循环的任务)并且它们的结果已保存在std::future
中。但是由于线程池的线程被阻塞,它们不能再执行任务了。因此,当前正在运行的任务等待执行其他任务,这些任务将永远不会被执行,因为所有线程都被阻止==&gt;僵局。
答案 1 :(得分:1)
迟到的答案,但我希望它有用。前段时间我开发了一个小而有效的threadpool library。 经过很少的修改,我已经测试了你的用例,它似乎工作得很好,所以也许你可以看一下。
Live demo。 (对不起,我必须在里面包含所有的库代码,用你的例子来测试它)