讲师中心 微信公众号
AI工具推荐 视频效率加速

c# ValueTaskSource 的实现和自定义异步返回类型

落婷大大_3488

落婷大大_3488

发布时间:2026-02-16 10:30:02

|

899人浏览过

|

来源于php中文网

原创

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

c# valuetasksource 的实现和自定义异步返回类型

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,但能跑通基础流程:

C#
C#

"避开 C# 常见错误——空陷阱、异步陷阱、LINQ 陷阱及资源泄漏。"

下载
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 实现里,文档极少,出错时堆栈也不友好。

热门AI工具

更多
Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

码上飞
码上飞 Hot

一款基于自然语言的AI应用开发平台,可根据用户描述自动生成应用程序,帮助个人和团队快速完成应用原型与开发。

相关专题

更多
登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

15086

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

1765

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

4217

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

6381

2024.03.01

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

7259

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3465

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

4548

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

3045

2025.08.29

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

0

2026.11.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn