valuetask 是按需懒加载的轻量替代品,同步返回时为零分配结构体,异步时才分配状态机;适用于高频调用且大量路径可同步完成的场景,如缓存命中、内存流读取等。

ValueTask 是什么,什么时候该用它
它不是 Task 的升级版,而是「按需懒加载的轻量替代品」:只有异步路径真要等待时,才分配状态机对象;同步返回时,就只是个结构体,零堆分配。
适合高频调用、且大量路径能同步完成的场景,比如缓存命中、内存流读取、简单条件判断后的立即返回。
反例:每次都要 await 数据库或 HTTP 请求——这种几乎必走异步分支,用 ValueTask 反而增加间接层,还可能因误复用引发 InvalidOperationException: "The ValueTask has already been consumed."。
如何正确声明和返回 ValueTask
必须用 async 方法 + ValueTask 返回类型,编译器会自动选择是否生成状态机:
– 同步路径用 return new ValueTask<t>(value)</t>
– 异步路径直接 await 并 return 表达式(编译器生成 ValueTask<t></t>)
– 不要手动 new ValueTask 包裹 Task,除非你明确需要延迟执行且控制生命周期(极少见)
– 别在非 async 方法里 return ValueTask,那只是普通结构体,await 会卡死或抛异常
public ValueTask<string> ReadNameAsync()
{
if (_nameCached != null)
return new ValueTask<string>(_nameCached); // 同步路径
return LoadFromDbAsync(); // async 方法,返回 ValueTask<string>
}</string></string></string>
await ValueTask 的坑:不能多次 await
ValueTask 是一次性消费的值类型,重复 await 会触发 InvalidOperationException,尤其容易发生在日志、异常处理、或条件分支中:
– 错误写法:if (vt.IsCompleted) { ... } await vt; await vt;
– 正确做法:只 await 一次,结果存到局部变量再复用
– 如果必须多次检查状态,用 AsTask() 转成引用类型(但失去零分配优势)
– 单元测试里也别直接 await 多次,容易漏掉这个约束
- 常见错误现象:
"The ValueTask has already been consumed." - 调试技巧:在 await 前加断点,观察
vt.IsCompleted和vt.IsFaulted状态 - 兼容性注意:.NET Core 2.1+ 支持,.NET Framework 不支持(只能用
Task)
IValueTaskSource 是什么,普通人要不要碰
它是 ValueTask 底层实现协议,用于自定义无栈异步操作(比如高性能网络库自己管理 I/O 完成队列),但绝大多数业务代码完全不需要实现它。
– 你写的 async 方法,编译器自动生成的 IValueTaskSource 实现已足够健壮
– 手动实现极易出错:需精确控制 OnCompleted、GetResult、线程安全、状态流转
– 除非你在写类似 System.IO.Pipelines 或自研异步运行时,否则看到这个接口就绕开
真正该花时间的地方,是理清自己方法里哪些分支能同步返回、哪些必然异步,以及是否值得为这点内存节省承担额外的使用约束。











