0

0

c# IValueTaskSource 的 GetStatus 和 OnCompleted

畫卷琴夢

畫卷琴夢

发布时间:2026-01-30 10:09:10

|

950人浏览过

|

来源于php中文网

原创

GetStatus 总返回 Pending 是正常行为,因它仅在 GetResult 能立即安全返回时才返回 Succeeded 或 Faulted;错误提前返回 Succeeded 会导致读取未初始化值或 NullReferenceException。

c# ivaluetasksource<t> 的 getstatus 和 oncompleted

GetStatus 为什么总返回 ValueTaskSourceStatus.Pending

这是最常被误解的地方:GetStatus 不是“状态轮询接口”,而是供 ValueTask 内部判断是否可安全获取结果的契约方法。它必须严格反映底层状态:只有在 GetResult 已能立即返回(即已完成且未抛异常)时,才可返回 ValueTaskSourceStatus.Succeeded;若已失败,返回 ValueTaskSourceStatus.Faulted;否则一律返回 Pending

常见错误是提前返回 Succeeded —— 比如在异步操作刚启动、结果尚未写入 _result 字段时就改状态,会导致 ValueTask.GetAwaiter().GetResult() 读到未初始化值或引发 NullReferenceException

  • GetStatus 返回 Pending 是常态,不是 bug
  • 状态变更必须与 SetResult/SetException 的调用严格同步(通常需加锁或用 Volatile.Write
  • 不要在 GetStatus 里做耗时检查(如轮询 IO 完成),它会被频繁调用

OnCompleted 的回调执行时机和线程约束

OnCompleted 接收一个 Action 和一个 object 状态对象,它的唯一职责是:当操作**最终完成**(无论成功/失败)时,确保该 Action 被调用一次。它不承诺执行线程,也不保证立即执行 —— 典型实现是把回调压入 ThreadPool 或当前 SynchronizationContext

关键点在于“最终完成”:如果操作本身是同步完成的(比如缓存命中),OnCompleted 可能根本不会被调用(因为 ValueTask 的 awaiter 会直接走 GetResult 分支);如果异步完成,则必须确保回调只触发一次,且不能漏掉。

  • 务必用 Interlocked.CompareExchangevolatile 标记完成状态,防止重复调用 OnCompleted 的回调
  • 不要在 OnCompleted 内阻塞或做重逻辑,它可能运行在 IOCP 线程或 UI 线程上
  • 若需调度到特定上下文(如 WinForms/WPF),应在回调内部手动 BeginInvoke,而非在 OnCompleted 里做

完整实现中容易漏掉的三个原子操作

手写 IValueTaskSource 时,90% 的崩溃来自状态竞争。以下三处必须原子化:

吉卜力风格图片在线生成
吉卜力风格图片在线生成

将图片转换为吉卜力艺术风格的作品

下载
  • 标记“已完成”的标志位(如 _completed 字段)—— 必须用 volatileInterlocked
  • 写入结果字段(_result)—— 必须在标记完成前写入,且对读取端可见(Volatile.WriteMemoryBarrier
  • 保存异常引用(_exception)—— 同样需内存屏障,避免重排序导致 GetResult 读到 null 异常

下面是一个最小可行的无锁结构体实现片段(省略泛型封装):

struct ManualResetValueTaskSource : IValueTaskSource
{
    private T _result;
    private Exception _exception;
    private volatile int _state; // 0=Pending, 1=Succeeded, 2=Faulted

    public ValueTaskSourceStatus GetStatus(short token) => 
        _state switch
        {
            1 => ValueTaskSourceStatus.Succeeded,
            2 => ValueTaskSourceStatus.Faulted,
            _ => ValueTaskSourceStatus.Pending
        };

    public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
    {
        if (_state != 0) // 已完成,直接触发
        {
            ThreadPool.UnsafeQueueUserWorkItem(continuation, state);
            return;
        }

        // 竞争设置为“正在完成”,仅一人成功
        if (Interlocked.CompareExchange(ref _state, 1, 0) == 0)
        {
            // 设置结果后才允许 GetResult 读取
            Volatile.Write(ref _result, default); // 占位,实际由 SetResult 填充
            ThreadPool.UnsafeQueueUserWorkItem(continuation, state);
        }
    }

    public T GetResult(short token) => _state switch
    {
        1 => _result,
        2 => throw _exception!,
        _ => throw new InvalidOperationException("Not completed")
    };

    public void SetResult(T result)
    {
        _result = result;
        Volatile.Write(ref _state, 1);
    }

    public void SetException(Exception ex)
    {
        _exception = ex;
        Volatile.Write(ref _state, 2);
    }
}

什么时候真该自己实现 IValueTaskSource

绝大多数场景不需要。.NET 6+ 的 TaskCompletionSource 已足够高效;ValueTask 的核心价值在于避免分配,而自定义 IValueTaskSource 的收益只在高频、短生命周期、纯内存操作的场景下才明显(例如高性能网络库中的连接池等待、无锁队列的出队等待)。

如果你只是想“让方法返回 ValueTask”,直接用 async Task + ConfigureAwait(false) 更安全;若用了 Task.FromResult,考虑换成 ValueTask(value) 构造函数即可。

真正需要手写的信号很明确:你正在压测发现 TaskCompletionSource 的 GC 分配成了瓶颈,且 profiler 显示大量 Task 对象存活在 Gen0,并确认这些等待几乎从不跨线程 —— 这时才值得投入精力。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

458

2024.03.01

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

240

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

192

2025.07.04

c++中volatile关键字的作用
c++中volatile关键字的作用

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

69

2025.10.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1133

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

213

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1845

2025.12.29

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

0

2026.01.30

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

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

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