c – 线程构建块:死锁因为所有线程都用完了

在英特尔线程构建块框架中,如何确保所有线程都不忙于等待其他线程完成.

考虑以下代码,

#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;
}

这里主要函数产生的任务是TBB库使用的线程池中有线程.然后,当在run_something函数中发生第二次产生更多任务的调用时,TBB调度程序会发现没有线程可用且只是死锁.那是我看到那个print语句在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()为您提供至少一个worker的承诺.

您的代码甚至不应该编译,因为您在main()中使用g.run()而不声明g.并且在调用wait()之前禁止销毁task_group,如the reference中针对析构函数所述:要求:必须在销毁task_group之前调用方法等待,否则析构函数会抛出异常.

至于共享线程池.是的,TBB有一个共享线程池.但您可以使用task_arena控制工作的共享方式.没有任务可以离开竞技场,但工作线程可以在运行任务之间的时间跨区域迁移.

点赞