静态构造函数中的异步任务等待行为会等待整个时间,即使任务已完成

async Task Wait behavior in static constructor waits for entire time even though task has finished

在静态构造函数中调用 Task.Run(..)..Wait(...) 时,即使任务已完成,它也会等待整个超时时间。只是好奇这是为什么?这种情况的最佳做法是什么?测试 class 如下所示

    static TestClass()
    {
        var delay = 100;
        var wait = 500;

        // Will wait the whole wait time
        var sw = Stopwatch.StartNew();
        Task.Run(() => DoStuff(delay)).Wait(wait);
        sw.Stop();
        var elapsedMs = sw.ElapsedMilliseconds;
        Debug.WriteLine($"Elapsed {elapsedMs}");
     
        // Will return when task is complete
        sw.Restart();
        Task.Run(()=>
        {
            var awaiter = Task.Run(() => DoStuff(delay)).GetAwaiter();
            var maxMs = delay * TimeSpan.TicksPerMillisecond;
            var swElapse = Stopwatch.StartNew();
            while (!awaiter.IsCompleted && swElapse.ElapsedTicks < maxMs)
            { }
            swElapse.Stop();
        }).Wait(wait);
        sw.Stop();
        elapsedMs = sw.ElapsedMilliseconds;
        Debug.WriteLine($"Elapsed {elapsedMs}");
    }

    static void DoStuff(int delay)
    {
        // Some async task called and waited for result
        Task.Run(async () => await Task.Delay(100)).GetAwaiter().GetResult();
    }
}
    var delay = 100;
    var maxMs = delay * TimeSpan.TicksPerMillisecond;
    var secondsTotal = maxMs / 1000.0m;
    Console.WriteLine("Total seconds thread will wait: " + secondsTotal + "s.");

当您 运行 此代码时,您将获得:1000s。 1000 秒是 16.7 分钟。

您的 while 条件是:

!awaiter.IsCompleted && swElapse.ElapsedTicks < maxMs

因为这是“与”运算,只要括号中的内容为真,while 循环就会继续。

在您的情况下,“awaiter.IsCompleted”的计算结果为真,不到 16 分钟的计算结果也为真(直到 16 分钟过去)。只有当其中一个条件为假时,while 循环才会停止重复。

这可能就是您遇到此行为的原因。

in a static constructor

我上次检查时,静态构造函数锁定,因为它们一次只能执行一个。因此,如果静态构造函数排队工作到另一个线程,然后该线程执行其他操作(即调用其他静态构造函数),而原始静态构造函数在另一个线程上被阻塞,那么you can end up with a deadlock 仅在超时时解决。

What is best practice for this scenario?

Don't block on async code,尤其是在构造函数中,尤其是 ,尤其是 ,在静态构造函数中更是如此。 (Link 是我的博客)。