任务沉池使用指南(TaskSink——SubmitFast / Submit)
定位:本仓库唯一合法的 fire-and-forget 任务积木。任何「发完即忘的高频轻量任务」 (RPC 发送/通知/即发即忘清理)都基于任务沉池构建——裸
_ = SomeAsync()是被禁止的形态 (unobserved exception 诊断黑洞 + Task 洪水打满公共池)。本文覆盖:两形态选型、API 全貌、 契约与硬性要求、仓库真实范例。长驻循环/队列消费见worker-loop.md; 要进度/结果句柄的长操作用 AsyncOperation 体系(Runtime 侧)。
0. 铁律:禁止裸 _ = SomeAsync()
裸丢弃的两个雷:
- 异常不可观测:任务挂了没有栈、没有日志,什么都没有(unobserved——楔死取证时 找不到失败记录)。
- Task 洪水:瞬间大量任务丢弃打满公共任务池——卡死、GC 停止。
任务沉池在提交点把两个雷都堵死:异常集中兜底(日志 + onFaulted + 故障计数)+
在途可观测(InFlightCount)+ Dispose 有界收尾(不悬挂半途任务)。
1. 两形态选型
SubmitFast(高性能路径) |
Submit(安全通用路径) |
|
|---|---|---|
| 启动 | 内联——任务体第一段(到第一个 await)在提交线程同步执行 | 池上——Task.Run 全程(含第一段),提交线程零占用 |
| 快路径 | 同步完成的任务 ≈ 0 开销(0 分配) | 无(每次固定 ~400ns 调度+包装) |
| 契约 | 第一段必须轻量非阻塞(阻塞拖住提交方) | 第一段任意(未知/第三方回调) |
选择树:要队列/限流/优先级 → WorkerLoop;要进度句柄 → AsyncOperation;
第一段轻量可控(自有代码)→ SubmitFast;第一段不可控 → Submit;
要身份/单任务取消/等待完成 → 不是本原语的语义(协议层自持状态;长稳定循环走
专用线程 + AsyncPump 泵域,时间驱动唤醒走 DeadlineRegistry——任务沉池只收
轻量高频即发即忘)。
2. 快速上手
// 装配(字段持有——Dispose 编排收尾)
private readonly TaskSink _sends = new(name: "raft-sends", logger: logger);
// 提交(fire-and-forget——void,零仪式;同步/异步上下文都可直接调)
_sends.SubmitFast(ct => _transport.SendAsync(peer, rpc, ct));
// 收尾(宿主 DisposeAsync 里编排——取消传播 + 有界等待在途)
await _sends.DisposeAsync();
构造参数(两形态同签名):
| 参数 | 默认 | 说明 |
|---|---|---|
string? name |
类名 | 诊断标识(日志区分多实例) |
ILogger? logger |
null | 未捕获异常兜底记录(null = 静默) |
Action<Exception>? onFaulted |
null | 未捕获异常回调(logger 之后调用——测试断言/高级消费方) |
TimeSpan? drainTimeout |
5s | Dispose 等在途完成的超时(超时 LogWarning 不抛——尽力而为) |
诊断属性:InFlightCount(在途数)/ SubmittedCount(累计提交)/ FaultedCount
(累计故障——洪水率与黑洞率可量化:Faulted/Submitted)。
3. 契约
一句话:提交即放手。
保证的:
- 异常收口不外泄:任务体漏 catch 由池兜底——
LogWarning+onFaulted+FaultedCount,不悬挂 unobserved。 - 组生命周期:Dispose = 取消传播(组级 ct)+ 有界 drain(等在途归零,超时告警不抛)。
- void 零仪式:提交无返回值、无句柄、无等待。
不保证的(fire-and-forget 的本义):
- 执行:已释放即拒(
ObjectDisposedExceptionfail-fast)、池拒绝、进程终—— 提交成功 ≠ 必然执行。 - 完成通知:丢弃静默,没有逐任务回调/句柄——只能事后从计数器观测
(
InFlightCount归零 /FaultedCount增量)。 - 顺序:提交序 ≠ 执行序(池上路径并发跑;内联路径第一段先行但续段回池)。
取消归一:所有 OperationCanceledException(组取消 Dispose 收尾/任务体业务取消)
归正常完成,不进故障计数。
4. 硬性要求
- 禁止裸
_ = SomeAsync()——一律经任务沉池(本文件 §0)。 SubmitFast的第一段契约——提交线程执行,必须轻量非阻塞;做不到换Submit或 WorkerLoop。- 宿主 Dispose 必须编排池收尾(
DisposeAsync传递)——不收尾则进程退出前在途 任务不可观测。 - 不做限流/排队(要排队语义用 WorkerLoop)——消费方井喷需自限;提交速率天然有界 的场景(单事件循环串行产生)无需处理。