actionblock仅实现itargetblock,不实现isourceblock,故无法linkto;必须用transformblock替代以支持输出;同步操作选action,异步i/o必用func;maxdegreeofparallelism不宜设unbounded,需配合boundedcapacity防oom;complete()仅关闸,须await completion确保处理完毕。

ActionBlock
为什么 ActionBlock.LinkTo() 总是报错?
因为 ActionBlock<tinput></tinput> 只实现 ITargetBlock<tinput></tinput>,不实现 ISourceBlock<toutput></toutput> —— 它天生就没有输出能力,所以不能作为上游被 LinkTo() 调用。
- 常见错误现象:
actionBlock.LinkTo(nextBlock)编译失败,提示 “ActionBlock<int></int>does not implementISourceBlock<t></t>” - 正确流向只能是单向:上游(如
TransformBlock、BufferBlock)→ActionBlock - 若想“加工后继续流转”,必须换用
TransformBlock<tinput toutput></tinput>,而不是硬塞逻辑进ActionBlock
构造 ActionBlock 时该选 Action 还是 Func?
取决于你的操作是否含异步 I/O(如 HTTP 请求、数据库写入、文件保存)。同步委托会阻塞线程池线程;异步委托才能真正释放线程。
- 同步场景(如纯内存计算、日志打点):用
Action<t></t>构造,简单高效 - 异步场景(如调用
HttpClient.PostAsync):必须用Func<t task></t>,否则会卡住整个并行队列 - 别混用:传
Action<t></t>却在内部await—— 编译器不会报错,但实际是同步执行 + 丢弃任务,极易引发超时或丢失数据
MaxDegreeOfParallelism 设成 Unbounded 会出事吗?
会。默认值是 1,设为 DataflowBlockOptions.Unbounded 表面看是“放开并发”,实则等于放弃反压控制,容易瞬间打爆下游服务或耗尽线程池。
- 真实建议值:4~16(视 I/O 类型和下游承载力而定),配合
BoundedCapacity使用 - 必须配
BoundedCapacity:否则即使限了并发,输入队列仍可无限堆积,OOM 风险极高 - 典型配置示例:
var block = new ActionBlock<string>(async s => { await SaveToDbAsync(s); }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 8, BoundedCapacity = 200 });</string>
Complete() 和 Completion.Wait() 的调用时机很关键
ActionBlock.Complete() 只是“关闸”,不等已入队消息执行完;Completion.Wait() 才真正等待所有消息处理完毕。漏掉后者,程序可能提前退出,导致最后几条消息丢失。
- 必须成对使用:
block.Post(x); ... block.Complete(); await block.Completion; - 别在
Post前就Complete(),否则后续Post会抛InvalidOperationException - 如果块已
Faulted(比如内部异常未捕获),Completion.Wait()会 re-throw 异常,需try/catch
最容易被忽略的是:ActionBlock 没有重试机制、没有死信队列、也不自动处理异常传播 —— 所有副作用失败都得你自己在委托里 try/catch,否则整个块会进入 Faulted 状态,再也不能接收新消息。











