valuetasksource 不是可实例化的类,而是指实现 ivaluetasksource 接口的一组类型;它支撑 valuetask 高效、无分配地完成同步/异步操作,需正确实现 5 个成员(含 Token 校验、version 递增、线程安全 oncompleted 等),并配合自定义 awaiter 才能被 await 消费。

ValueTaskSource 是什么,为什么不能直接 new
ValueTaskSource 不是一个可实例化的类,而是指实现 IValueTaskSource 接口的一组类型。.NET 的 ValueTask 本身不持有状态,它只在构造时绑定一个 IValueTaskSource 实例(或 Task)。你无法直接 new ValueTaskSource(),因为它是接口,且底层依赖特定的同步上下文、完成机制和状态管理。
自定义异步返回类型的关键,是提供一个高效、无分配、支持同步/异步混合完成的 IValueTaskSource 实现 —— 常见于高性能库(如 System.IO.Pipelines 或自研零分配 I/O 层)。
手写 IValueTaskSource 的最小必要成员
实现 IValueTaskSource 至少要覆盖 5 个方法,其中 3 个带 short token 参数(用于区分多次 await),2 个用于获取结果。漏掉任一都会导致 ValueTask 行为异常(如重复完成、GetResult 报 InvalidOperationException)。
-
GetResult(short token):必须检查token是否匹配当前完成时传入的 token,否则多 await 场景下会读到错误结果 -
OnCompleted(Action<Object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)</object>:必须安全存储continuation和state,并在完成时调用;flags决定是否需捕获上下文(FlowExecutionContext) -
Version属性:每次完成必须递增,用于 runtime 校验是否重复 await 同一个已完成实例
注意:short token 不是“用户传入的 ID”,而是 ValueTask 内部生成的唯一标记,你的实现**绝不能忽略它或硬编码为 0**。
一个线程安全的简单实现示例(无锁但用 interlocked)
以下是一个仅支持单次完成、线程安全、无内存分配的 IValueTaskSource<int></int> 示例。它不处理取消、超时、多 await,但能跑通基础流程:
public sealed class SimpleIntSource : IValueTaskSource<int> { private volatile int _state; // 0=not completed, 1=completed private int _result; private Action<object> _continuation; private object _stateObject; private short _version; <pre class='brush:php;toolbar:false;'>public int GetResult(short token) { if (token != _version) throw new InvalidOperationException("Invalid token"); return _result; } public ValueTaskSourceStatus GetStatus(short token) => token == _version ? ValueTaskSourceStatus.Succeeded : ValueTaskSourceStatus.Pending; public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { if (Interlocked.CompareExchange(ref _state, 1, 0) == 0) { _continuation = continuation; _stateObject = state; _version = token; } else { // 已完成,立即调度 continuation(注意:这里没做 ExecutionContext 捕获) ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(state), null); } } public void SetResult(int result) { _result = result; var version = Interlocked.Increment(ref _version); if (Interlocked.Exchange(ref _state, 1) == 0 && _continuation != null) { _continuation(_stateObject); } } public short Version => _version;
}
使用方式:
var source = new SimpleIntSource(); var t = new ValueTask<int>(source); // … later source.SetResult(42); var r = await t; // 得到 42
⚠️ 容易踩的坑:
– 没检查 token 导致 await 多次时 GetResult 返回旧值或抛异常
– OnCompleted 中未处理已完状态,导致 continuation 永远不被调用
– 忘记递增 Version,runtime 会拒绝 await(报 InvalidOperationException: Attempted to await a ValueTask with an invalid token)
自定义异步返回类型需要额外做什么
如果想让 await myCustomType 成立,除了实现 IValueTaskSource<t></t>,你还得提供一个“包装器类型”并为其添加 GetAwaiter() 方法 —— 因为 C# 编译器只认 GetAwaiter,不直接查 IValueTaskSource。
- 该包装器通常是个
Struct(避免分配),内部持有一个IValueTaskSource<t></t>引用或内联数据 -
GetAwaiter()返回一个实现了INotifyCompletion的 struct(例如ValueTaskAwaiter<t></t>的简化版) - 编译器生成的
await代码最终调用的是这个 awaiter 的IsCompleted、GetResult和OnCompleted
也就是说:自定义类型 ≠ 自定义 IValueTaskSource,而是「自定义类型 + 自定义 awaiter + 可选的 IValueTaskSource 实现」三者配合。多数场景下,直接返回 ValueTask<t></t> 并用私有 IValueTaskSource 实现支撑,是最简洁可控的做法。
真正难的不是写完这几十行代码,而是确保所有路径(同步完成、异步完成、并发 await、异常完成、取消)都满足 ValueTask runtime 的严格契约 —— 这些细节藏在 System.Private.CoreLib 的 ValueTask 实现里,文档极少,出错时堆栈也不友好。