news 2026/9/14 14:27:15

mold 项目内嵌 TBB Flow Graph 的 sender 模板类:消息发送方接口与弃用迁移指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
mold 项目内嵌 TBB Flow Graph 的 sender 模板类:消息发送方接口与弃用迁移指南

mold 项目内嵌 TBB Flow Graph 的 sender 模板类:消息发送方接口与弃用迁移指南

【免费下载链接】moldmold: A Modern Linker 🦠项目地址: https://gitcode.com/GitHub_Trending/mo/mold

导读

sender<T>是 oneTBB Flow Graph 中定义"消息发送方"行为的抽象基类,也是理解整个 flow graph 数据流方向的起点。本文以 mold 仓库中随附的 oneTBB 官方规范文档 sender_cls.rst 为主体,完整梳理该模板类的语法、头文件、成员语义与设计要点,并结合仓库内 flow_graph.h 的实际实现说明其底层行为,同时给出register_successor已弃用、应改用make_edge/remove_edge的迁移指南。读完本文,你将能够准确理解 sender 接口的每个虚函数职责、如何在自定义节点中继承并实现 sender,以及如何正确构建和拆除图中的边。

说明:本文讨论的sender类来自 mold 仓库内嵌的 oneTBB(Threading Building Blocks)第三方库,该库随 mold 一同分发,路径位于 third-party/tbb。虽然 mold 是高性能链接器,其内嵌 TBB 主要用于自身并行化,但 TBB Flow Graph 是完整独立的数据流编程框架,本文仅聚焦 flow graph 的 sender 接口这一主题。

概述:sender 在 Flow Graph 中的角色

在 oneTBB Flow Graph 模型中,图由**节点(node)边(edge)**组成,消息沿着边从生产者流向消费者。sender<T>就是描述"能够向外发送类型为T的消息"的节点的抽象基类。与之对应的是receiver<T>,描述"能够接收类型为T的消息"的节点。

根据规范文档的定义:

An abstract base class for nodes that act as message senders.

即:sender 是"充当消息发送方"的节点的抽象基类。它同时提供了若干函数的默认实现(default implementations),因此派生节点只需实现必要的最小接口即可。

从 flow_graph.h 的源码注释可以看到,oneTBB 将 sender 描述为 "Pure virtual template class that defines a sender of messages of type T"——一个定义"类型 T 的消息发送方"的纯虚模板类。这里的T即节点输出消息的类型。

需要特别注意的是,规范文档在开头就给出了caution(警告)

This feature is deprecated and will be reworked or removed in the future.

也就是说,sender类本身已被标记为弃用(deprecated),未来可能被重构或移除。这一点在使用时应心中有数。

语法与头文件

模板声明

template< typename T > class sender;

sender是一个以消息类型T为模板参数的类模板,使用位置如sender<int>sender<my_message>等。

头文件

#include "oneapi/tbb/flow_graph.h"

在 mold 仓库中,该头文件的真实路径为 third-party/tbb/include/oneapi/tbb/flow_graph.h,同目录下还提供了传统路径的兼容头文件 third-party/tbb/include/tbb/flow_graph.h。注意,实际实现位于oneapi::tbb::detail::d2命名空间,并通过头文件底部的命名空间别名机制暴露到oneapi::tbb::flow,这与文档给出的使用命名空间一致:

namespace oneapi { namespace tbb { namespace flow { // 用户实际使用的 sender 位于此命名空间 } } }

完整成员定义

规范文档给出了sender的完整成员列表(见 sender_cls.rst):

template< typename T > class sender { public: typedef T output_type; typedef receiver<output_type> successor_type; virtual ~sender(); virtual bool register_successor( successor_type &r ) = 0; virtual bool remove_successor( successor_type &r ) = 0; virtual bool try_get( output_type &v ) { return false; } virtual bool try_reserve( output_type &v ) { return false; } virtual bool try_release( ) { return false; } virtual bool try_consume( ) { return false; } };

与仓库内 flow_graph.h 的实现在成员集合上完全一致(仓库版本将output_typesuccessor_type放在protected区,并将两个纯虚函数设为protected以配合register_successor/remove_successor的友元自由函数,这是实现细节上的差异,接口语义不变)。

其中:

类型别名含义
output_type本 sender 输出的消息类型,即模板参数T
successor_type后继节点类型,即receiver<output_type>,表示"能够接收本节点输出消息的接收方"

成员函数语义表

规范文档用一张表逐项说明了每个成员的语义(见 sender_cls.rst),整理如下:

成员说明返回值
~sender()虚析构函数
bool register_successor( successor_type &r ) = 0纯虚方法,定义"向 sender 的后继集合中添加一个后继节点"的接口添加成功返回true,否则返回false
bool remove_successor( successor_type &r ) = 0纯虚方法,定义"从 sender 的后继集合中移除一个后继节点"的接口移除成功返回true,否则返回false
bool try_get( output_type &v )从 sender 请求一个消息项默认实现返回false
bool try_reserve( output_type &v )在 sender 处保留(reserve)一个消息项默认实现返回false
bool try_release( )释放 sender 上持有的保留(reservation)默认实现返回false
bool try_consume( )消耗 sender 上持有的保留(reservation)默认实现返回false

两类成员的不同性质

理解这张表的关键在于区分两类成员:

  1. 纯虚函数(必须实现)register_successorremove_successor= 0的纯虚函数,任何直接继承sender<T>的自定义节点都必须提供实现,否则无法实例化。它们负责维护"后继节点集合"这一核心数据结构,是消息转发路径的根基。

  2. 虚函数(可选覆写)try_gettry_reservetry_releasetry_consume均有默认实现且默认返回false。这意味着:默认情况下,sender 既不支持被"拉取"(pull),也不支持"保留-消耗"(reserve/consume)协议;只有覆写它们、返回true并真正实现相应行为的节点,才支持这些高级交互方式。

try_get / try_reserve / try_release / try_consume 的交互协议

规范文档分别描述了这四个函数的功能,它们共同构成一个完整的消息保留协议

  • try_get(v):请求一个消息项,成功时把消息写入v并返回true
  • try_reserve(v):请求"保留"一个消息项,成功时写入v并返回true。保留意味着该项暂时锁定,不能被其他消费者取走。
  • try_release():释放此前通过try_reserve取得的保留,消息项重新变为可获取状态。
  • try_consume():将此前保留的消息项正式消耗掉(移除),完成消费。

典型流程为:try_reserve(v)成功后,消费者可选择try_consume()(真正取走)或try_release()(放弃)。在 flow_graph.h 中,limiter_nodebuffer_nodequeue_nodeoverwrite_node等缓冲类节点都覆写了这套协议,以支持基于保留的消息调度(例如reserving_arc形式的边会调用try_reserve决定是否可建立输送)。

源码实现佐证:从 sender 到边

核心实现位置

mold 仓库内嵌 oneTBB 中 sender 的核心实现位于 flow_graph.h,关键点如下:

  • 类注释明确其为 "Pure virtual template class";
  • 每个虚函数都带有简要注释,如//! Request an item from the sender//! Reserves an item in the sender//! Releases the reserved item//! Consumes the reserved item
  • 纯虚函数register_successor/remove_successor被声明为protected,并分别声明了两个模板友元函数register_successor(sender<C>&, receiver<C>&)remove_successor(sender<C>&, receiver<C>&)(见 flow_graph.h),自由函数内部只是简单转发到对应的成员函数。

典型实现:broadcast_node / buffer_node

仓库中许多具体节点都继承自sender<T>(同时继承receiver<T>),例如:

  • broadcast_node:注释为 "Forwards messages of type T to all successors",把收到的每个消息转发给所有后继,内部用broadcast_cache<input_type>维护后继集合;
  • buffer_node:同时继承reservable_item_bufferreceiver<T>sender<T>,是一个可缓冲、支持保留协议的消息管道节点;
  • queue_nodeoverwrite_nodesource_nodemultifunction_nodecomposite_node等同样通过继承 sender/receiver 获得收发能力(composite_nodeoutput_ports_type定义为std::tuple< sender<OutputTypes>&... >,见 flow_graph.h)。

可以推断:一个节点"既能收又能发"时,通常同时继承receiver<T>sender<T>;而纯数据源节点(如source_node)则主要作为 sender 存在。

边的构建与拆除:make_edge / remove_edge

规范文档在 Description 一节给出了第二个caution(重要警告)

The use ofregister_successorto build graphs has been deprecated forflow::graph. Graphs should be constructed withmake_edgeandremove_edge. Replacen1.register_successor(n2)withmake_edge(n1,n2).

即:使用register_successor构建图已被弃用,应改用make_edgeremove_edge构建与拆除边;将n1.register_successor(n2)替换为make_edge(n1, n2)

仓库实现印证了这一点:flow_graph.h 中make_edge经由internal_make_edge调用register_successor(p, s)再记录 profiling 事件(fgt_make_edge);remove_edge 则调用remove_successor(p, s)并记录fgt_remove_edge。也就是说,make_edge/remove_edge正是对弃用接口的安全封装,内部仍复用后继集合的增删逻辑,但对外提供了更干净、更安全的 API,并且支持多种重载:

  • make_edge(sender<T>&, receiver<T>&):单输入单输出节点之间建边;
  • 当节点具有output_ports_type/input_ports_type(多端口节点,如multifunction_nodesplit_nodeindexer_node)时,自动选取端口 0 建边(见 flow_graph.h),remove_edge提供完全对称的重载集合(见 flow_graph.h)。

迁移指南:从 register_successor 到 make_edge

根据规范文档的明确指示,把旧式建边代码迁移到新式 API 非常简单:

迁移前(已弃用):

// 旧式:直接操作后继集合 n1.register_successor(n2); n1.remove_successor(n2);

迁移后(推荐):

// 新式:通过自由函数建边/拆边 make_edge(n1, n2); remove_edge(n1, n2);

注意事项:

  • make_edgeremove_edge要求两端的消息类型匹配(sender<T>连到receiver<T>),类型不匹配会在编译期报错,这也是比直接调用register_successor更安全的原因之一;
  • 对多端口节点,make_edge/remove_edge会自动处理端口 0 的接线,无需手动从output_ports()/input_ports()中取端口;
  • 虽然register_successor/remove_successor仍是纯虚函数、任何 sender 派生类都必须实现,但用户代码不应直接调用它们来建图;make_edge内部会完成转发与 profiling 记录。

自定义 sender 派生类的实践要点

若需要在 mold 仓库内嵌的 TBB 中编写自定义节点并使其具备发送能力,可以从 sender 的接口设计中得到以下实践要点:

  1. 必须实现register_successor(successor_type&)remove_successor(successor_type&),用一个容器(如std::vector<successor_type*>或 TBB 提供的broadcast_cache<T>round_robin_cache<T>等后继缓存类)维护后继集合;
  2. 默认即可编译运行:不覆写try_get/try_reserve/try_release/try_consume时它们返回false,节点表现为"只推不拉、不支持保留";
  3. 按需覆写拉取与保留协议:需要支持下游拉取(pull-based)或保留式调度(如连接limiter_node)时,再覆写try_get及 reserve/release/consume 三件套,并保证状态机一致(reserve 后必须 consume 或 release 二选一);
  4. 建图一律走make_edge/remove_edge,不要直接调用register_successor
  5. 注意弃用风险sender类整体已被标记为 will be reworked or removed,若编写面向未来的新代码,建议尽量基于高层节点(function_nodebuffer_nodemultifunction_nodecomposite_node等)组合图结构,而非直接继承sender

验证与测试

mold 仓库内嵌的 oneTBB 自带完整测试套件,位于 third-party/tbb/test(含test_flow_graph*.cpp等用例),以及文档目录 third-party/tbb/doc/main/specification(本文主体 sender_cls.rst 即位于其中的uncategorized/flow_graph分类下,同目录还收录了receiver_cls.rst等配套规范)。如需在本地验证 sender 相关行为,可参考 TBB 的构建方式(其 CMake 构建入口为 third-party/tbb/CMakeLists.txt)编译 flow graph 相关测试,或直接阅读头文件中的接口注释与节点实现来确认语义。

小结

sender<T>作为 oneTBB Flow Graph 的发送端抽象基类,通过"两个纯虚函数 + 四个带默认实现的虚函数"的极简接口,统一了所有消息发送方节点的行为契约:register_successor/remove_successor负责后继集合的维护,try_get/try_reserve/try_release/try_consume定义拉取与保留协议。在 mold 仓库内嵌的 flow_graph.h 中,broadcast_nodebuffer_nodecomposite_node等节点都以它为基础构建,而make_edge/remove_edge则成为取代弃用接口、安全构建数据流图的推荐途径。理解 sender 的接口设计,是深入掌握 Flow Graph 消息传递机制、乃至自定义节点行为的关键一步。

【免费下载链接】moldmold: A Modern Linker 🦠项目地址: https://gitcode.com/GitHub_Trending/mo/mold

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 14:27:02

同一把 TaoToken Key,MarsCode 从 Doubao-1.5-pro 切到 DeepSeek-R1

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 14:26:29

五颗芯片打造工业设备监测与远程IO控制终端,双MCU架构全解析

前阵子做项目选型&#xff0c;手头同时到了一批芯片&#xff1a;TLE7272-2D、GD32F427VGT6、STM32F417ZGT6、MCP4631-503E/ST、GRX350A3BC160。乍一看这就是一张“购物车清单”&#xff0c;但把它们铺到原理图工作区后我发现&#xff0c;这五颗器件刚好能组成一套完整的智能系统…

作者头像 李华
网站建设 2026/9/14 14:26:18

ASP.NET MVC 5学生宿舍管理系统开发实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 14:25:46

Java多版本共存与切换的完整实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 14:24:14

Python幂函数拟合实战:用curve_fit与最小二乘法精准建模

简介&#xff1a;面向需要快速搭建幂函数拟合任务的Python数据分析初学者与科研人员&#xff0c;压缩包提供一段可直接运行的curve_fit示例代码&#xff0c;围绕ya*x^b模型演示完整的数据分析链路。脚本先导入numpy、matplotlib和scipy.optimize等常用库&#xff0c;再定义幂函…

作者头像 李华
网站建设 2026/9/14 14:22:56

校园一卡通系统实战:SpringBoot+Vue+MySQL架构与事务并发设计

简介&#xff1a;一份基于 Java SpringBoot、Vue 与 MySQL 的校园一卡通系统毕业设计项目&#xff0c;面向计算机专业学生&#xff0c;可直接用于毕设、课程设计或期末大作业。系统覆盖身份验证、门禁管理、图书借阅、食堂消费等场景&#xff0c;配备用户管理、财务报表、数据统…

作者头像 李华