news 2026/7/6 1:20:10

深入理解C++ Workflow源码(1)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入理解C++ Workflow源码(1)

第1篇 一个 Task 是如何诞生的

很多人第一次接触 Workflow,会觉得它“用起来很轻”,但真正神奇的地方不在 API 表面,而在于一个 Task 从创建、初始化、调度到回调结束,几乎把异步编程里最麻烦的几件事都藏起来了。

这一篇我们不急着钻 epoll,也不急着看协议细节,只回答一个问题:

用户写下WFTaskFactory::create_http_task()之后,框架内部到底发生了什么?


一、先看用户代码:Task 是从工厂出来的

Workflow 的设计非常明确:任务由工厂创建,业务由任务流组织

典型代码大概长这样:

auto*task=WFTaskFactory::create_http_task("http://www.example.com",3,2,[](WFHttpTask*task){if(task->get_state()==WFT_STATE_SUCCESS){protocol::HttpResponse*resp=task->get_resp();// 处理响应}});task->start();

从这段代码里,至少能看到四个事实:

  1. 用户并不直接new某个底层任务对象。
  2. 用户看到的是强类型任务,比如WFHttpTaskWFMySQLTask
  3. 用户只写 callback,不处理线程、fd、连接池。
  4. 一个任务本身就能start(),说明它最终一定能挂进某种统一调度模型。

这正是 Workflow 的第一层抽象:所有异步操作先被包装成 SubTask,再由 Workflow 把它们编排成 Series 或 Parallel。


二、Task 的“壳”:为什么一切都先变成 SubTask

在内核层,最底层的抽象不是 HTTP,也不是 MySQL,而是SubTask

classSubTask{public:virtualvoiddispatch()=0;private:virtualSubTask*done()=0;protected:voidsubtask_done();};

这个接口很小,但语义非常重:

  • dispatch():把任务真正投递出去。
  • done():任务结束后,告诉框架“下一个该跑谁”。
  • subtask_done():统一推进整个任务流。

也就是说,Workflow 并不是“回调到回调”的设计,而是“当前 SubTask 完成后,返回下一个 SubTask”的设计。
这和很多传统异步框架最大的不同在于:控制流并没有散掉,而是被收敛在统一的任务推进器里。


三、Task 的“流”:为什么 start() 其实是在启动一个 Series

无论是线程任务、网络任务还是定时器任务,start()的实现都非常像:

voidstart(){assert(!series_of(this));Workflow::start_series_work(this,nullptr);}

这段代码说明了一个非常关键的设计:

一个独立任务启动时,并不是“裸奔”的,它会先被包进一个串行流SeriesWork

这样做有三个好处:

  1. 单个任务和复杂工作流共享同一套执行模型。
  2. callback 结束后对象销毁、上下文传递、取消语义都能统一。
  3. 后续动态往 series 里push_back()新任务变得非常自然。

换句话说,Workflow 不是“任务系统 + 工作流系统”两套机制,而是一套机制从一开始就覆盖了最简单到最复杂的场景。


四、Task 工厂到底创建了什么

以 HTTP 为例,WFTaskFactory暴露的是工厂接口:

usingWFHttpTask=WFNetworkTask<protocol::HttpRequest,protocol::HttpResponse>;

WFHttpTask不是一个具体类,而是一个类型别名。真正被创建出来的,通常是某个更复杂的实现类,例如HttpTaskImpl.cc里的ComplexHttpTask

classComplexHttpTask:publicWFComplexClientTask<HttpRequest,HttpResponse>{protected:virtualCommMessageOut*message_out();virtualCommMessageIn*message_in();virtualintkeep_alive_timeout();virtualboolinit_success();virtualvoidinit_failed();virtualboolfinish_once();};

这层实现很重要,因为它说明 Workflow 的任务对象并不是“请求包 + 回调”这么简单,而是一个封装了以下能力的复杂状态机

  • URI 解析
  • DNS/路由选择
  • 连接建立
  • SSL 握手
  • 请求编码
  • 响应解码
  • 重定向
  • 重试
  • keep-alive

所以,从用户视角看是“一个 HTTP 任务”,从框架视角看其实是“一个隐藏了多个异步阶段的复合任务”。


五、Task 为什么能统一成几种基类

Workflow 把任务大致收敛成三条主线:

template<classINPUT,classOUTPUT>classWFThreadTask:publicExecRequest{...};template<classREQ,classRESP>classWFNetworkTask:publicCommRequest{...};classWFTimerTask:publicSleepRequest{...};

这三类分别对应:

  • 计算任务:交给Executor + thrdpool
  • 网络任务:交给CommScheduler + Communicator
  • 定时器任务:交给Communicator的 timer 机制

这也是 Workflow 很漂亮的一点:
上层 API 很丰富,但底层入口非常少。

丰富的协议和组件,最后都会落到少数几种可调度任务上。


六、一个 HTTP Task 的诞生流程

把源码串起来后,一个 HTTP Task 的出生过程大致是下面这样:

第 1 步:用户调用工厂

WFTaskFactory::create_http_task(...)

工厂负责创建具体实现对象,并把 callback、重试次数、重定向次数这些策略参数灌进去。

第 2 步:具体任务初始化

ComplexHttpTask构造时会设置默认方法、版本等基础状态:

client_req->set_method(HttpMethodGet);client_req->set_http_version("HTTP/1.1");

第 3 步:start() 包装成 Series

任务调用start()后,被Workflow::start_series_work()包成一个最小串行流。

第 4 步:dispatch() 投递到底层调度器

如果是网络任务,会进入CommRequest::dispatch()

if(this->scheduler->request(this,this->object,this->wait_timeout,&this->target)<0){this->handle(CS_STATE_ERROR,errno);}

第 5 步:Communicator 驱动整个异步生命周期

后面连接、发送、接收、解析、超时、错误处理都由Communicatorpoller接管。

第 6 步:done() 回到 Series

任务完成后,会执行 callback,然后回到 series 取下一个任务:

if(this->callback)this->callback(this);deletethis;returnseries->pop();

这一步非常关键:Workflow 不是“回调结束就结束”,而是“回调结束后决定工作流下一步怎么走”。


七、为什么说 Workflow 的 Task 是“结构化并发单元”

很多异步框架里的 task,更像是一段“待执行回调”。
Workflow 里的 task 则更像一个完整的并发单元,因为它天然带着这些能力:

  • 有明确的生命周期:创建到 callback 结束
  • 有统一的归属:一定属于某个 series
  • 有清晰的完成语义:通过done()交出控制权
  • 有统一的调度入口:dispatch()
  • 有强类型输入输出:请求/响应对象都挂在任务上

这使得 Workflow 的任务既能表达“做一次网络请求”,也能表达“作为工作流中的一个节点存在”。


八、从“一个 Task 的诞生”里能看出什么设计思想

读到这里,其实已经能看出 Workflow 的几条核心设计哲学:

1. 用户接口丰富,内核抽象极简

上层有 HTTP/MySQL/Redis/Timer/FileIO/Graph 等等,底层却尽量收敛到:

  • SubTask
  • SeriesWork
  • CommRequest
  • ExecRequest

2. 把复杂异步过程隐藏到“一个任务”里

HTTP 任务里可能包含 DNS、连接、SSL、重试、重定向,但用户只看到一个 Task。

3. 通过 series 统一生命周期和控制流

单任务和复杂工作流用同一套机制,所以没有“简单场景一套逻辑,复杂场景另一套逻辑”的割裂感。

4. 不把并发模型暴露给业务代码

业务代码写 callback 即可;线程、fd、epoll、连接复用都由框架兜底。


九、这篇先记住三句话

如果你第一次读 Workflow 源码,我建议先把下面三句话记住:

  1. Task 的真正基类是SubTask,不是 HTTPTask。
  2. 独立 Task 启动时会先被包进SeriesWork
  3. 一个“看起来简单”的任务,内部通常是一个复合状态机。

理解了这三点,后面看SeriesWorkWFNetworkTaskCommunicator时,脑子里就不会把它们当成互相独立的模块,而会把它们看成同一条链路上的不同层。


十、下一篇看什么

既然 Task 一出生就会被包进SeriesWork,那下一个问题自然就是:

Series 到底是什么?为什么 Workflow 要把所有任务都放进一个串行流里?

下一篇,我们就专门拆开SeriesWork看它如何管理队列、推进后继任务、处理取消,以及如何跟并行任务拼成更大的工作流。

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

百度网盘秒传脚本终极指南:彻底解决文件分享失效的完整方案

百度网盘秒传脚本终极指南&#xff1a;彻底解决文件分享失效的完整方案 【免费下载链接】rapid-upload-userscript-doc 秒传链接提取脚本 - 文档&教程 项目地址: https://gitcode.com/gh_mirrors/ra/rapid-upload-userscript-doc 还在为百度网盘分享链接频繁失效而烦…

作者头像 李华
网站建设 2026/7/6 1:15:17

国内EMBA哪个好?2026综合实力TOP5权威评测榜单

在企业全球化布局、产业数字化转型的行业趋势下&#xff0c;越来越多大中华区企业创始人、高管不再局限于内地传统EMBA项目&#xff0c;转而选择兼具国际化视野、双语教学优势与国内学历认可度的境外优质EMBA课程。市面上EMBA项目数量繁多&#xff0c;办学资质、师资实力、课程…

作者头像 李华
网站建设 2026/7/6 1:15:14

Huiwen Han —— 论文与预印本目录 2026年7月

独立研究计划&#xff0c;聚焦"受治理的人工智能自主性"的哲学与工程问题&#xff0c;同时涉及软件工程、信息论与数学领域。 最后更新&#xff1a;2026 年 7 月 AI 治理、控制与自主性 核心研究方向&#xff1a;真正"治理"一个 AI Agent 意味着什么——…

作者头像 李华
网站建设 2026/7/6 1:13:05

OC7141 PWM 调光 LED 驱动器:3A 输出下 60uA 静态电流的 PCB 布局 3 要点

OC7141 PWM调光LED驱动器&#xff1a;3A大电流与60uA超低静态电流的PCB设计实战在LED驱动电路设计中&#xff0c;OC7141以其独特的线性降压架构和PWM调光功能&#xff0c;成为众多硬件工程师的首选。这款能够在3A输出电流下保持60uA超低静态电流的驱动器&#xff0c;对PCB布局提…

作者头像 李华