mold 仓库中的 oneAPI TBB task_completion_handle 指南:动态任务依赖与单任务完成等待的完整实现解析 📅 发布时间:2026/9/14 21:00:14 👁 浏览次数: mold 仓库中的 oneAPI TBB task_completion_handle 指南动态任务依赖与单任务完成等待的完整实现解析【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold导读task_completion_handle是 oneAPI TBBThreading Building Blocks任务组扩展中的核心句柄类型用于表示一个任务以便设置执行依赖并追踪其完成状态。与task_handle提交后即变空不同task_completion_handle无论任务处于已提交、执行中还是已完成状态都始终持有对该任务的引用。本文以 task_completion_handle_cls.rst 为骨架结合仓库内 TBB 头文件源码_task_handle.h、task_group.h、task_arena.h与官方示例完整讲解其 API、与动态依赖及单任务等待两大扩展功能的协同关系使读者能够掌握在 TBB 任务组中建立动态依赖图、等待单个任务完成以及转移任务完成点的完整实战能力。说明本仓库mold 链接器项目将 TBB 作为第三方依赖引入位于third-party/tbb本文讨论的task_completion_handle即来源于该依赖是理解任务组扩展功能的参考文档。预备知识扩展宏与头文件该功能属于preview 扩展使用前必须定义宏#define TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 #include oneapi/tbb/task_group.h在task_completion_handle之外同一扩展还提供了两个配套特性动态依赖Dynamic Dependencies通过task_group::set_task_order建立任务间前驱/后继关系见 dynamic_dependencies.rst单任务等待Waiting for Individual Tasks通过wait_for_task/run_and_wait_for_task/get_status_of等待单个任务完成见 wait_single_task.rst。当宏启用时编译器会额外定义特性测试宏TBB_HAS_TASK_GROUP_DEPENDENCIES动态依赖可用TBB_HAS_TASK_GROUP_WAIT_FOR_SINGLE_TASK单任务等待可用。从源码看该宏控制着 task_group.h 中run(task_handle)的依赖检查分支以及task_group_status枚举中的task_complete成员见 _task_handle.henum task_group_status { not_complete, complete, canceled #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS , task_complete #endif };task_completion_handle 与 task_handle 的本质区别理解task_completion_handle的关键是它与task_handle生命周期语义的不同。官方文档给出了一个逐步演示两者差异的代码序列tbb::task_group tg; tbb::task_handle th tg.defer(task_body); // task is not submitted // th is non-empty and represents the task tbb::task_completion_handle tch th; // task is not submitted // both th and tch are non-empty and represent the task tg.run(std::move(th)); // task is submitted // th is empty // tch is non-empty and keeps representing the task tg.wait(); // task is completed // tch is non-empty and represents the completed task可以看到task_handle是一次性所有权句柄一旦通过run提交任务句柄即变为空task_completion_handle是跟踪型句柄任务从创建到提交再到完成它始终非空并保持对该任务的引用。从源码层面看这种差异源于两者的内部表示_task_handle.htask_handle内部持有std::unique_ptrtask_handle_task, task_handle_task_deleter提交时通过task_handle_accessor::release释放底层任务指针见 task_group.h因此提交后句柄变空task_completion_handle内部持有指向task_dynamic_state的指针m_task_state通过引用计数reserve()/release()与任务动态状态建立共同所有权关系见 _task_handle.h从而在任务提交、执行、完成后都能继续引用它。task_completion_handle的两大用途建立动态任务依赖可作为task_group::set_task_order的前驱随时添加后继任务即使对应任务已经提交甚至已完成等待单个任务完成通过task_group::wait_for_task等函数单独等待该任务而不必等待组内所有任务结束。非空task_completion_handle的获取途径从非空task_handle构造或赋值、复制另一个非空的task_completion_handle。多个task_completion_handle可以同时引用同一个任务空的task_completion_handle不引用任何任务。完整 API 参考头文件与类声明#define TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 #include oneapi/tbb/task_group.h namespace oneapi { namespace tbb { class task_completion_handle { public: task_completion_handle(); task_completion_handle(const task_handle handle); task_completion_handle(const task_completion_handle other); task_completion_handle(task_completion_handle other); ~task_completion_handle(); task_completion_handle operator(const task_handle handle); task_completion_handle operator(const task_completion_handle other); task_completion_handle operator(task_completion_handle other); explicit operator bool() const noexcept; friend bool operator(const task_completion_handle lhs, const task_completion_handle rhs) noexcept; friend bool operator!(const task_completion_handle lhs, const task_completion_handle rhs) noexcept; friend bool operator(const task_completion_handle t, std::nullptr_t) noexcept; friend bool operator!(const task_completion_handle t, std::nullptr_t) noexcept; friend bool operator(std::nullptr_t, const task_completion_handle t) noexcept; friend bool operator!(std::nullptr_t, const task_completion_handle t) noexcept; }; // class task_completion_handle } // namespace tbb } // namespace oneapi需要说明的是源码中的类位于tbb::detail::d2命名空间见 _task_handle.h而公共头文件通过using detail::d2::task_completion_handle;将其暴露在oneapi::tbb命名空间见 task_group.h。构造函数task_completion_handle();默认构造一个空的task_completion_handle不引用任何任务。task_completion_handle(const task_handle handle);构造引用handle所关联任务的task_completion_handle。若handle为空行为未定义UB。源码中通过断言__TBB_ASSERT(th, ...)检查这一前提并调用get_dynamic_state()惰性创建任务的动态状态同时reserve()增加一份共同所有权引用_task_handle.h。task_completion_handle(const task_completion_handle other);拷贝构造。拷贝后*this与other引用同一个任务other的m_task_state被复制并reserve()新增一份共同所有权引用因此两个句柄可同时存活。task_completion_handle(task_completion_handle other);移动构造。移动后*this引用other原先引用的任务other被置空other.m_task_state nullptr不产生新的所有权引用。析构函数~task_completion_handle();销毁句柄。若其仍引用某任务则调用m_task_state-release()释放共同所有权引用当引用计数归零时动态状态对象被销毁_task_handle.h。赋值操作符task_completion_handle operator(const task_handle handle);将*this引用的任务替换为handle关联的任务。若handle为空行为未定义。返回*this。源码实现在替换前先释放旧状态的引用再对新状态reserve()_task_handle.h。task_completion_handle operator(const task_completion_handle other);拷贝赋值。赋值后两者引用同一任务。若新旧状态相同m_task_state ! other.m_task_state判断则不做多余操作否则先释放旧引用再获取新引用。返回*this。task_completion_handle operator(task_completion_handle other);移动赋值。移动后*this引用other原先的任务other置空。返回*this。观察者explicit operator bool() const noexcept;若*this引用某个任务则返回true否则返回false。源码实现为m_task_state ! nullptr。由于是explicit只能用于条件判断等布尔上下文不能隐式转换为整数。比较操作bool operator(const task_completion_handle lhs, const task_completion_handle rhs) noexcept;若lhs与rhs引用同一个任务则返回true。源码通过比较m_task_state指针实现_task_handle.h。bool operator!(const task_completion_handle lhs, const task_completion_handle rhs) noexcept;等价于!(lhs rhs)。bool operator(const task_completion_handle t, std::nullptr_t) noexcept; bool operator(std::nullptr_t, const task_completion_handle t) noexcept;若t不引用任何任务则返回true即空句柄判断。bool operator!(const task_completion_handle t, std::nullptr_t) noexcept; bool operator!(std::nullptr_t, const task_completion_handle t) noexcept;等价于!(t nullptr)。需要注意在 C20 及以后__TBB_CPP20_COMPARISONS_PRESENT部分比较运算符由编译器自动推导源码中以条件编译控制_task_handle.h。场景一作为动态依赖的前驱task_completion_handle最核心的价值体现在动态依赖功能中。相关 API 定义于 dynamic_dependencies.rst要点如下未提交任务unsubmitted尚未提交执行的任务已提交任务submitted已通过task_group::run等提交执行的任务非空task_handle只能表示未提交任务而task_completion_handle可以表示任意状态的任务已提交和未提交的任务都可以作为前驱但只有未提交的任务才能作为后继。set_task_order的两个重载static void set_task_order(task_handle pred, task_handle succ); static void set_task_order(task_completion_handle pred, task_handle succ);两者都调用pred_state-add_successor(succ)task_group.h。add_successor的源码实现_task_handle.h体现了该扩展的核心机制为后继任务动态状态register_dependency()注册一个依赖计数通过notify_successor_node把后继挂到前驱的通知链表notify list上前驱完成时fetch_list_and_notify_all(COMPLETED_FLAG)遍历链表递减后继的依赖计数最后一个依赖释放时后继任务被spawn执行_task_handle.h。因为依赖信息保存在任务自身的task_dynamic_state中而非task_handle中所以即使前驱已被提交执行只要持有其task_completion_handle就能随时添加后继——这正是动态依赖名称的由来。线程安全性方面可以并发地为同一后继添加多个前驱也可以把同一前驱注册给多个后继可以在同一任务的task_handle被run的同时并发地向其task_completion_handle添加后继。未定义行为的情形包括pred或succ为空前后驱任务属于不同的task_group实例被task_completion_handle引用的任务在未提交执行前就被销毁。场景二转移任务完成点transfer_this_task_completion_to允许把当前正在执行任务的完成状态转移给另一个任务static void transfer_this_task_completion_to(task_handle handle);该函数必须从任务体内调用。转移后当前任务的所有后继会被重新挂接到接收完成状态的任务上即后继任务要等接收者完成后才会执行。文档中的示例tbb::task_handle t tg.defer([tg] { tbb::task_handle comp_receiver tg.defer(receiver_body); tbb::task_group::transfer_this_task_completion_to(comp_receiver); tg.run(std::move(comp_receiver)); }); tbb::task_handle succ tg.defer(succ_body); tbb::task_group::set_task_order(t, succ); // Since t transfers its completion to comp_receiver, // succ_body will execute after receiver_body源码实现路径为transfer_this_task_completion_to→dynamic_state_task::transfer_completion_to→task_dynamic_state::transfer_completion_to_task_handle.h先把通知链表头 CAS 为TRANSFERRED_FLAG标记取出整个通知链表再整体并入接收任务的通知链表并把新完成点记录在m_new_completion_point中。此后无论是新增后继add_successor、等待完成wait_for_completion还是查询状态get_task_status一旦发现TRANSFERRED_FLAG都会自动重定向到接收任务。未定义行为的情形handle为空在task_group任务体外调用对已完成转移的任务再次调用当前任务与handle关联任务属于不同task_group。场景三等待单个任务完成配合task_completion_handleTBB 还提供了单任务等待功能定义于 wait_single_task.rst。它补充了传统task_group::wait()等待组内所有任务的粒度让调用者只等待感兴趣的那一个任务。相关 API// oneapi/tbb/task_group.h synopsis namespace oneapi { namespace tbb { enum task_group_status { not_complete, complete, canceled, task_complete }; class task_group { public: task_group_status wait_for_task(task_completion_handle comp_handle); task_group_status run_and_wait_for_task(task_handle handle); task_group_status get_status_of(task_completion_handle comp_handle); }; } }// oneapi/tbb/task_arena.h synopsis namespace oneapi { namespace tbb { class task_arena { public: task_group_status wait_for(task_completion_handle comp_handle); }; } }task_group_status 枚举语义枚举值语义not_complete工作尚未完成。由get_status_of返回时表示该任务尚未完成由wait/run_and_wait返回时表示组内并非所有任务都已完成complete组未被取消且组内所有任务均已完成canceled已收到取消请求。由get_status_of或单任务等待函数返回时表示该任务未被执行由wait/run_and_wait返回时表示组执行被取消、各任务完成状态未知task_complete该任务已完成执行。此时task_group的取消状态未知——即使组收到取消请求个别任务仍可能被执行成员函数详解task_group_status wait_for_task(task_completion_handle comp_handle);阻塞等待comp_handle代表的任务完成。若完成点已通过transfer_this_task_completion_to转移则等待接收完成的任务。返回任务被执行则返回task_group_status::task_complete否则返回canceled。源码实现为state-wait_for_completion(context())task_group.h其内部通过notify_waiter_node把等待者挂入通知链表然后d1::wait阻塞_task_handle.h。task_group_status run_and_wait_for_task(task_handle handle);提交handle代表的任务若无未解决依赖并等待其完成。语义等价于task_completion_handle ch handle; run(std::move(handle)); wait_for_task(ch);返回task_complete或canceled。源码internal_run_and_wait_for_tasktask_group.h中若任务仍有依赖且释放依赖后计数未归零则只等待否则直接执行自身并等待。task_group_status get_status_of(task_completion_handle comp_handle);不阻塞地查询任务状态not_complete任务未提交或尚未执行完毕task_complete任务已执行完毕canceled任务因task_group取消而未执行。若完成点被转移则返回接收任务的状态。源码实现task_dynamic_state::get_task_status_task_handle.h直接检查通知链表头的哨兵标记COMPLETED_FLAG/CANCELED_FLAG/TRANSFERRED_FLAG无需阻塞。task_group_status wait_for(task_completion_handle comp_handle); // task_arena 成员在当前 arena 中等待comp_handle代表的任务完成。语义等价于在对应task_group上调用wait_for_taskexecute([] { tg.wait_for_task(comp_handle); })。返回task_complete或canceled。该函数同样在完成点被转移时等待接收任务。完整示例缓存计算官方示例 task_group_extensions_wait_for_one.cpp 演示了缓存场景calculate_one_result先查缓存缓存未命中时用run_and_wait_for_task立即计算并返回结果同时把缓存写入作为异步任务提交到同一task_group全部结果算完后tg.wait()确保后台缓存写入全部完成后再清空缓存#define TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 #include oneapi/tbb/task_group.h int calculate_one_result(tbb::task_group tg, int input) { int result 0; // Check cache first if (cache_lookup(input, result)) { return result; } // No cached item - run result calculation // May build task graph with dependencies tbb::task_handle calculate_task tg.defer(calculate_result(input, result)); tg.run_and_wait_for_task(std::move(calculate_task)); // Result is calculated - run asynchronous caching tg.run(cache_result(input, result)); // Return the result to caller return result; } void calculate_all_results(const std::vectorint inputs, std::vectorint results) { tbb::task_group tg; for (std::size_t i 0; i inputs.size(); i) { results[i] calculate_one_result(tg, inputs[i]); } // Wait for all incomplete caching tasks before clear tg.wait(); clear_cache(); }这个例子同时体现了等待单个任务run_and_wait_for_task与等待组内全部任务tg.wait()两种粒度的配合使用。综合实战并行归约Parallel Reduction动态依赖、完成点转移与task_completion_handle三者可以组合出完整的任务依赖图。官方示例 task_group_extensions_reduction.cpp 展示了用该 API 实现并行求和#define TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 #include oneapi/tbb/task_group.h struct reduce_task { struct join_task { void operator()() const { result *left *right; } std::size_t result; std::unique_ptrstd::size_t left; std::unique_ptrstd::size_t right; }; tbb::task_handle operator()() const { tbb::task_handle next_task; std::size_t size end - begin; if (size serial_threshold) { // Perform serial reduction for (std::size_t i begin; i end; i) { result i; } } else { // The range is too large to process directly // Divide it into smaller segments for parallel execution std::size_t middle begin size / 2; auto left_result std::make_uniquestd::size_t(0); auto right_result std::make_uniquestd::size_t(0); tbb::task_handle left_leaf tg.defer(reduce_task{begin, middle, *left_result, tg}); tbb::task_handle right_leaf tg.defer(reduce_task{middle, end, *right_result, tg}); tbb::task_handle join tg.defer(join_task{result, std::move(left_result), std::move(right_result)}); tbb::task_group::set_task_order(left_leaf, join); tbb::task_group::set_task_order(right_leaf, join); tbb::task_group::transfer_this_task_completion_to(join); // Save the left leaf for further bypassing next_task std::move(left_leaf); tg.run(std::move(right_leaf)); tg.run(std::move(join)); } return next_task; } std::size_t begin; std::size_t end; std::size_t result; tbb::task_group tg; }; std::size_t calculate_parallel_sum(std::size_t begin, std::size_t end) { tbb::task_group tg; std::size_t reduce_result 0; tg.run_and_wait(reduce_task{begin, end, reduce_result, tg}); return reduce_result; }该示例的关键点set_task_order(left_leaf, join)与set_task_order(right_leaf, join)为join建立两个前驱依赖transfer_this_task_completion_to(join)把当前父任务的完成点转移给join使父任务的后继若有要等join完成任务体内返回task_handle使调度器可以进行任务旁路bypass优化直接执行后继而无需重新入队。与 task_arena 的集成动态依赖扩展还涉及task_arena的两个enqueue重载见 dynamic_dependencies.rst// oneapi/tbb/task_arena.h synopsis namespace oneapi { namespace tbb { class task_arena { // Only the behavior in case of dependent tasks is changed void enqueue(task_handle handle); }; namespace this_task_arena { // Only the behavior in case of dependent tasks is changed void enqueue(task_handle handle); } } }二者行为与泛型enqueue(F f)一致仅参数类型不同。扩展语义为若任务有前驱则调度推迟到所有前驱完成后进行函数立即返回。使用时handle必须非空否则行为未定义。对应地task_arena::wait_for(task_completion_handle)用于在 arena 内等待单个任务完成task_arena.h。这使依赖图与 arena 的排队执行模型可以协同工作。使用建议与注意事项综合文档与源码实践中需要特别注意必须先定义TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1且要在包含头文件之前定义task_completion_handle从空task_handle构造或赋值是未定义行为使用前应通过operator bool或与nullptr比较确认句柄非空任务属于创建它的task_groupset_task_order的跨组使用是未定义行为被task_completion_handle引用的任务不能未提交就被销毁否则破坏动态状态的所有权关系transfer_this_task_completion_to只能在任务体内调用且不能重复转移源码中task_dynamic_state的引用计数机制m_num_references保证了task_handle与多个task_completion_handle可安全共存析构顺序由计数自动管理_task_handle.h如需深入理解通知链表的无锁实现可阅读add_notify_nodeCAS 插入 完成/取消/转移三种哨兵标记处理与fetch_list_and_notify_all遍历通知并批量唤醒后继的源码_task_handle.h。延伸阅读类参考文档task_completion_handle_cls.rst动态依赖特性dynamic_dependencies.rst单任务等待特性wait_single_task.rst旁路bypass支持task_bypass_support.rst核心实现头文件_task_handle.h公共接口task_group.h、task_arena.h可运行示例task_group_extensions_reduction.cpp、task_group_extensions_wait_for_one.cpp、task_group_extensions_bypassing.cpp【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考