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

AsyncLocal<Activity?>在无await的async方法中为何导致Activity父级丢失?

异步方法无await时Activity父级丢失问题解析

示例代码

public class Worker
{
    private static readonly ActivitySource Source = new ActivitySource("Worker");

    public static void DoWork()
    {
        var work = Source.StartActivity();
        // <----------------------------------put a breakpoint here
    } 
    
    /*
    public static async Task DoWork()
    {
        var work = Source.StartActivity();
        // <----------------------------------put a breakpoint here
    } 
    */
}

internal class Program
{
    static void Main(string[] args)
    {
        using var listener = new ActivityListener
        {
            ShouldListenTo = source => true,
            Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllData
        };

        ActivitySource.AddActivityListener(listener);

        Worker.DoWork();
        Worker.DoWork();
    }
}

问题现象

  • 当Worker.DoWork()为静态无返回值方法时,第二次调用生成的Activity会以第一次的Activity为父级,符合预期。
  • 将其改为public static async Task DoWork()(未使用await关键字)后,第二次调用生成的Activity无父级,尽管方法是同步执行的。

原因分析

核心问题出在AsyncLocal<Activity?> s_current的上下文管理逻辑,以及async方法的编译特性:

  1. 普通同步方法的上下文行为
    同步方法中,调用StartActivity()会将当前Activity赋值给s_current,方法执行完毕后不会主动重置这个值。因此第二次调用时,s_current保留着第一次的Activity实例,自然会作为新Activity的父级。

  2. async方法的状态机特性
    哪怕async方法里没有await关键字,.NET编译器仍会为它生成异步状态机。这个状态机在执行时会做两个关键操作:

    • 进入方法时,保存当前的AsyncLocal上下文快照;
    • 方法执行完毕后,自动恢复进入方法前的AsyncLocal状态。

第一次调用async版本的DoWork()时,进入方法前s_current是null,状态机执行完后会把s_current重置回null。因此第二次调用时,s_current已经没有父Activity的引用,新生成的Activity就没有父级了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 23:15:54