TBB中如何避免第二个parallel_for_each调用时复用主线程?
避免TBB复用主线程执行后台任务的解决方案
从你的代码和遇到的问题来看,核心痛点是TBB的默认调度逻辑会把调用并行API的主线程也纳入工作线程池,导致原本该在后台跑的task_group任务被后续的parallel_for_each调度到主线程上,触发了UI线程检查的断言。我来给你拆解问题和对应的解决办法:
问题根源
TBB的默认任务调度器为了最大化CPU利用率,会将**调用parallel_for/parallel_for_each的线程(包括主线程)**当作工作线程来执行任务队列里的待处理任务。当你在主线程先启动了task_group但没调用wait(),后续主线程又调用parallel_for_each时,TBB会把主线程分配去处理task_group里未完成的任务,这就直接踩中了你UI线程检查的逻辑。而标准库的并行函数默认不会征用主线程做工作线程,所以不会出现这个问题。
最优解决方案:用tbb::task_arena隔离后台任务
task_arena是TBB提供的任务隔离工具,它可以创建一个独立的线程池,把指定任务限制在这个arena内执行,完全杜绝主线程被复用的情况。修正后的代码如下:
using namespace std::chrono_literals; #include <tbb/task_arena.h> #include <tbb/task_group.h> #include <tbb/parallel_for_each.h> #include <eastl/vector.h> #include <atomic> #include <thread> #include <cassert> int main() { // 创建独立的后台任务arena,默认用CPU核心数创建线程池,也可手动指定线程数(如task_arena(4)) tbb::task_arena background_arena; tbb::task_group group; const auto mainThreadId = std::this_thread::get_id(); std::atomic<bool> needWait{true}; std::atomic<bool> taskOnThreadFinish{false}; // 将task_group的所有任务提交到后台arena执行 background_arena.execute([&] { group.run([&] { eastl::vector<int> values(20, 0); tbb::parallel_for_each( values.begin(), values.end(), [&](int& /*i*/) { while (needWait) std::this_thread::sleep_for(1ms); // 现在这个断言永远不会触发了 assert(mainThreadId != std::this_thread::get_id()); std::this_thread::sleep_for(1ms); }); taskOnThreadFinish = true; }); // 必须在arena内部调用wait,确保task_group的任务都在后台线程池完成 group.wait(); }); // 主线程的parallel_for_each只会使用默认线程池,不会征用主线程去处理后台任务 eastl::vector<int> mainThreadValues(10, 0); tbb::parallel_for_each( mainThreadValues.begin(), mainThreadValues.end(), [&](int& /*i*/) { needWait = false; std::this_thread::sleep_for(1ms); }); while (!taskOnThreadFinish) std::this_thread::sleep_for(1ms); return 0; }
关键细节说明
task_arena的线程池是独立于默认线程池的,后台任务完全在这个专属池子里执行,和主线程的并行任务互不干扰- 在
arena.execute()内部调用group.wait()是必要的,否则task_group的未完成任务可能被溢出到默认线程池(包括主线程) - 如果需要更精细的控制,可以在创建
task_arena时指定线程数量,比如task_arena(2)限制后台只用2个线程
其他可选思路(不推荐)
你也可以尝试用tbb::global_control设置max_allowed_parallelism,但这种方式只是限制并行度,无法彻底阻止主线程被征用,隔离性远不如task_arena,所以不推荐在UI场景下使用。
内容的提问来源于stack exchange,提问作者Francois
相关产品推荐
相关产品推荐

