需要一些关于 async/await 和线程安全的解释

Per*_*t28 2 c# async-await

还有一个关于线程安全和 async/await 的问题。我不明白为什么我的第二个增量不是线程安全的。

class Program
{
    static int totalCountB = 0; 
    static int totalCountA = 0;
    
    static async Task AnotherTask()
    {
        totalCountA++; // Thread safe
        await Task.Delay(1);
        totalCountB++; // not thread safe 
    }

    static async Task SpawnManyTasks(int taskCount)
    {
        await Task.WhenAll(Enumerable.Range(0, taskCount)
            .Select(_ => AnotherTask()));
    }

    static async Task Main()
    {
        await SpawnManyTasks(10_000);

        Console.WriteLine($"{nameof(totalCountA)} : " + totalCountA); // Output 10000
        Console.WriteLine($"{nameof(totalCountB)} : " + totalCountB); // Output 9856
    }
}
Run Code Online (Sandbox Code Playgroud)

我的理解是:

  • totalCountA++是线程安全的,因为在那之前,代码是完全同步的。
  • 我知道await可能会在线程池上运行,但我没想到恢复的代码await将完全是多线程的。

根据一些答案/博客,async/await 不应创建新线程:

我真的在这里错过了一些大事。

Ste*_*ary 6

TotalCountA++ 是线程安全的,因为在那之前,代码是完全同步的。

是的。这是因为async方法开始在调用线程上执行,就像同步方法一样(归因:我)。

我知道await可能会在线程池上运行,但我没想到恢复await的代码将完全是多线程的。

await不会“运行”到任何地方。这就是您链接到的我的《没有线索》文章的要点。

现在,在之后await,其余代码需要在某个地方运行。await默认情况下将捕获“上下文”并使用它来执行方法的其余部分async归属:我)。该“上下文”是SynchronizationContext.Current,除非它是null,在这种情况下它是TaskScheduler.Current。在控制台应用程序中,这意味着上下文是线程池上下文,因此任何可用的线程池线程都将执行该方法的其余部分async