c# IValueTaskSource 是什么 c#如何实现自定义ValueTask

来源:这里教程网 时间:2026-02-21 17:39:59 作者:

ValueTask 不是“轻量级 Task”,而是可选的异步结果容器

直接说结论:

ValueTask
本身不是 awaitable 的“实现”,它只是一个包装器,背后要么持有一个
Task
,要么持有一个实现了
IValueTaskSource
的对象。只有当你要**完全绕过 Task 分配、手动控制异步状态机流转**时,才需要实现
IValueTaskSource
—— 这不是日常开发该碰的东西,而是为高性能库(如
System.IO.Pipelines
Microsoft.Extensions.Caching
)底层服务的机制。

IValueTaskSource 是什么:一个极简但危险的异步状态契约

IValueTaskSource
是一个仅含 5 个成员的接口,它把
await
行为拆解成纯方法调用:谁来决定是否已完成、如何获取结果、怎么注册回调、怎么触发完成。它不依赖
ThreadPool
SynchronizationContext
,一切由你控制。

关键点:

Version
字段必须每次完成时递增,否则
ValueTask
会因版本不匹配而抛出
InvalidOperationException
GetResult(short token)
必须只被调用一次,且仅在
IsCompleted
返回
true
后;若未完成就调用,行为未定义
OnCompleted
注册的回调,必须在你调用
SetResult
/
SetException
时被同步或异步执行 —— 且必须严格匹配传入的
token
你必须自己管理线程安全:多个线程可能并发调用
OnCompleted
SetResult

手写一个最简 IValueTaskSource:只支持成功、无取消、单次使用

下面是一个仅用于演示的最小可行实现,它模拟“稍后返回一个 int”。注意:它不处理取消、不支持重用、不保证线程安全(仅作原理示意):

public sealed class SimpleIntSource : IValueTaskSource<int>
{
    private int _result;
    private short _version;
    private Action<object?>? _continuation;
    private object? _state;
    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)
    {
        _continuation = continuation;
        _state = state;
    }
    public void SetResult(int result)
    {
        _result = result;
        _version++;
        _continuation?.Invoke(_state);
    }
    public void SetException(Exception error) => throw new NotSupportedException();
    public short Version => _version;
}

用法示例:

public static ValueTask<int> DelayedInt() {
    var source = new SimpleIntSource();
    _ = Task.Run(() => {
        Thread.Sleep(100);
        source.SetResult(42);
    });
    return new ValueTask<int>(source);
}

为什么你不该在业务代码里实现 IValueTaskSource

真正棘手的地方不在接口签名,而在运行时契约:

ValueTask
可能被多次 await(只要没调用
GetResult
),但你的
IValueTaskSource
实例通常只能完成一次;重复 await 同一个已完成的
ValueTask
会传入旧
token
,导致
GetResult
校验失败
没有内置取消支持,要加
CancellationToken
就得自己维护
OperationCanceledException
路径和取消注册逻辑
无法与
async/await
状态机自动集成:你不能在
async
方法里
return new ValueTask<t>(mySource)</t>
并指望编译器帮你生成正确状态机 —— 它只认
Task
ValueTask
构造函数中传入的
IValueTaskSource
所有 .NET 内建 API(如
Stream.ReadAsync
)返回的
ValueTask
都封装了高度优化、经过压测的
IValueTaskSource
实现(如
ReadValueTaskSource
),它们复用缓冲区、跳过调度、内联回调 —— 自己写的几乎不可能比它们更优

除非你在写类似

PipeReader.ReadAsync
这种每秒百万级调用的基础组件,否则直接用
Task.FromResult
Task.Delay
或标准
async
方法更安全、更易维护。

相关推荐