You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么自定义异步方法构建器仅作为类或Debug模式下正常运行?

问题根因

你遇到的问题本质是值类型(struct)的拷贝语义和C#异步状态机的优化逻辑共同作用的结果:

  • 你直接把_exception、_hasResult、_lock、_result、_source这些可变状态存在了struct类型的构建器本身的字段里。而C#编译器在Release模式优化异步状态机的过程中,会在装箱、状态机上下文传递时对struct类型的构建器执行值拷贝,导致你后续的SetResult/SetException操作是在拷贝后的新struct实例上执行的,和之前访问Task属性的struct实例是完全独立的两个对象,各自的字段状态完全不互通,自然就出现了修改不可见、SpinLock不生效的竞态问题。
  • Debug模式下编译器会关闭异步状态机的相关优化,所有操作都指向同一个struct实例,所以不会出现该问题;你把构建器改成class(引用类型)后,拷贝的只是对象引用,所有操作都指向同一个实例,也不会有问题。
  • 微软官方的AsyncTaskMethodBuilder<TResult>虽然也是struct,但它本身不存储可变状态,所有的结果、异常、完成状态都存在内部持有的引用类型Task<TResult>实例上,struct就算被拷贝,内部的引用还是指向同一个Task对象,所以不会出现状态不共享的问题。
修复方案

有两种可行的修复方式:

  1. 直接将CustomAwaitableAsyncMethodBuilder<T>定义为class,你已经验证过该方案可以正常运行。
  2. 保持构建器为struct,将所有可变状态封装到一个独立的引用类型中,构建器仅持有该引用类型的实例:
// 封装所有可变状态的引用类型
internal sealed class BuilderState<T>
{
    public Exception? Exception;
    public bool HasResult;
    public SpinLock Lock = new SpinLock(Debugger.IsAttached);
    public T? Result;
    public TaskCompletionSource<T>? Source;
}

public struct CustomAwaitableAsyncMethodBuilder<T>
{
    private readonly BuilderState<T> _state = new BuilderState<T>();

    public CustomAwaitable<T> Task
    {
        get
        {
            var lockTaken = false;
            try
            {
                _state.Lock.Enter(ref lockTaken);
                if (_state.Exception is not null)
                    return new CustomAwaitable<T>(ValueTask.FromException<T>(_state.Exception));
                if (_state.HasResult)
                    return new CustomAwaitable<T>(ValueTask.FromResult(_state.Result!));
                return new CustomAwaitable<T>(
                    new ValueTask<T>(
                        (_state.Source ??= new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously))
                        .Task
                    )
                );
            }
            finally
            {
                if (lockTaken)
                    _state.Lock.Exit();
            }
        }
    }

    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter,
        ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine =>
        awaiter.OnCompleted(stateMachine.MoveNext);

    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter,
        ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine =>
        awaiter.UnsafeOnCompleted(stateMachine.MoveNext);

    public static CustomAwaitableAsyncMethodBuilder<T> Create() => new();

    public void SetException(Exception exception)
    {
        var lockTaken = false;
        try
        {
            _state.Lock.Enter(ref lockTaken);
            if (Volatile.Read(ref _state.Source) is {} source)
            {
                source.TrySetException(exception);
            }
            else
            {
                _state.Exception = exception;
            }
        }
        finally
        {
            if (lockTaken)
                _state.Lock.Exit();
        }
    }

    public void SetResult(T result)
    {
        var lockTaken = false;
        try
        {
            _state.Lock.Enter(ref lockTaken);
            if (Volatile.Read(ref _state.Source) is {} source)
            {
                source.TrySetResult(result);
            }
            else
            {
                _state.Result = result;
                _state.HasResult = true;
            }
        }
        finally
        {
            if (lockTaken)
                _state.Lock.Exit();
        }
    }

    public void SetStateMachine(IAsyncStateMachine stateMachine) {}

    public void Start<TStateMachine>(ref TStateMachine stateMachine)
        where TStateMachine : IAsyncStateMachine => stateMachine.MoveNext();
}

这样就算struct构建器被拷贝,所有副本持有的都是同一个BuilderState<T>实例,状态完全共享,就不会出现竞态和状态不一致的问题。

内容的提问来源于stack exchange,提问作者Matt Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 16:39:03