线程构建块:死锁,因为所有线程都用完了
Thread Building Blocks: Deadlocks because all threads used up
在英特尔线程构建块框架中,如何确保所有线程不忙于等待其他线程完成。
例如考虑以下代码,
#include <tbb/tbb.h>
#include <vector>
#include <cstdlib>
#include <future>
#include <iostream>
std::future<bool> run_something(std::function<bool(bool)> func, bool b) {
auto task = std::make_shared<std::packaged_task<bool()> >(std::bind(func, b));
std::future<bool> res = task->get_future();
tbb::task_group g;
g.run([task]() { (*task)(); });
return res;
};
int main() {
tbb::parallel_for(0, 100, 1, [=](size_t i) {
g.run([] () {
std::cout << "A" << std::endl;
run_something([] (bool b) { return b; }, true).get();
});
});
return EXIT_SUCCESS;
}
此处 main
函数作为任务产生,因为 TBB 库使用的线程池中有线程。然后,当在 run_something
函数中第二次调用生成更多任务时,TBB 调度程序发现没有线程可用,并且简单地死锁。也就是说,我看到该打印语句在 4 超线程机器上正好通过 4 次,在 8 超线程机器上通过 8 次。
我该如何避免这种情况,特别是有没有办法确保两个 task_group
或 task_arena
或 parallel_for
构造使用两个完全不相交的线程集?
std::future
与 TBB 的可选并行模式不兼容。 std::future::get()
真的应该命名为 let_me_block_in_system_wait_here()
。除非您希望 TBB 死锁,否则禁止在 TBB 任务调度程序不知道的 TBB 任务之间实现任何类型的同步。也就是说,使用 TBB 方法表达 TBB 任务之间的依赖关系。
可选的并行性意味着您的代码必须正确 运行 仅使用单个线程。只有 tbb::task::enqueue()
承诺除了主线程外至少有一个工作线程。
您的代码甚至不应该编译,因为您在 main()
中使用了 g.run()
而没有声明 g
。并且禁止在调用wait()
之前销毁task_group
,如the reference中声明的析构函数:Requires: Method wait must be called before destroying a task_group, otherwise the destructor throws an exception.
至于共享线程池。是的,TBB 有一个共享线程池。但是您可以使用 task_arena
控制作品的共享方式。没有任务可以离开竞技场,但工作线程可以在 运行 任务之间的时间内跨竞技场迁移。
在英特尔线程构建块框架中,如何确保所有线程不忙于等待其他线程完成。
例如考虑以下代码,
#include <tbb/tbb.h>
#include <vector>
#include <cstdlib>
#include <future>
#include <iostream>
std::future<bool> run_something(std::function<bool(bool)> func, bool b) {
auto task = std::make_shared<std::packaged_task<bool()> >(std::bind(func, b));
std::future<bool> res = task->get_future();
tbb::task_group g;
g.run([task]() { (*task)(); });
return res;
};
int main() {
tbb::parallel_for(0, 100, 1, [=](size_t i) {
g.run([] () {
std::cout << "A" << std::endl;
run_something([] (bool b) { return b; }, true).get();
});
});
return EXIT_SUCCESS;
}
此处 main
函数作为任务产生,因为 TBB 库使用的线程池中有线程。然后,当在 run_something
函数中第二次调用生成更多任务时,TBB 调度程序发现没有线程可用,并且简单地死锁。也就是说,我看到该打印语句在 4 超线程机器上正好通过 4 次,在 8 超线程机器上通过 8 次。
我该如何避免这种情况,特别是有没有办法确保两个 task_group
或 task_arena
或 parallel_for
构造使用两个完全不相交的线程集?
std::future
与 TBB 的可选并行模式不兼容。 std::future::get()
真的应该命名为 let_me_block_in_system_wait_here()
。除非您希望 TBB 死锁,否则禁止在 TBB 任务调度程序不知道的 TBB 任务之间实现任何类型的同步。也就是说,使用 TBB 方法表达 TBB 任务之间的依赖关系。
可选的并行性意味着您的代码必须正确 运行 仅使用单个线程。只有 tbb::task::enqueue()
承诺除了主线程外至少有一个工作线程。
您的代码甚至不应该编译,因为您在 main()
中使用了 g.run()
而没有声明 g
。并且禁止在调用wait()
之前销毁task_group
,如the reference中声明的析构函数:Requires: Method wait must be called before destroying a task_group, otherwise the destructor throws an exception.
至于共享线程池。是的,TBB 有一个共享线程池。但是您可以使用 task_arena
控制作品的共享方式。没有任务可以离开竞技场,但工作线程可以在 运行 任务之间的时间内跨竞技场迁移。