0

0

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

畫卷琴夢

畫卷琴夢

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

|

839人浏览过

|

来源于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>:必须安全存储 continuationstate,并在完成时调用;flags 决定是否需捕获上下文(FlowExecutionContext
  • Version 属性:每次完成必须递增,用于 runtime 校验是否重复 await 同一个已完成实例

注意:short token 不是“用户传入的 ID”,而是 ValueTask 内部生成的唯一标记,你的实现**绝不能忽略它或硬编码为 0**。

一个线程安全的简单实现示例(无锁但用 Interlocked)

以下是一个仅支持单次完成、线程安全、无内存分配的 IValueTaskSource<int></int> 示例。它不处理取消、超时、多 await,但能跑通基础流程:

搜狐资讯
搜狐资讯

AI资讯助手,追踪所有你关心的信息

下载
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 的 IsCompletedGetResultOnCompleted

也就是说:自定义类型 ≠ 自定义 IValueTaskSource,而是「自定义类型 + 自定义 awaiter + 可选的 IValueTaskSource 实现」三者配合。多数场景下,直接返回 ValueTask<t></t> 并用私有 IValueTaskSource 实现支撑,是最简洁可控的做法。

真正难的不是写完这几十行代码,而是确保所有路径(同步完成、异步完成、并发 await、异常完成、取消)都满足 ValueTask runtime 的严格契约 —— 这些细节藏在 System.Private.CoreLibValueTask 实现里,文档极少,出错时堆栈也不友好。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

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

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

6392

2023.09.14

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

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

835

2023.09.14

token怎么获取
token怎么获取

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

1084

2023.12.21

token什么意思
token什么意思

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

1567

2024.03.01

string转int
string转int

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

750

2023.08.02

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

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

568

2024.08.29

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

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

234

2025.08.29

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

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

209

2025.08.29

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

145

2026.02.13

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 9.5万人学习

C 教程
C 教程

共75课时 | 4.7万人学习

C++教程
C++教程

共115课时 | 17.9万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号