异步线程体循环,它只是工作,但如何?

Async thread body loop, It just works, but how?

我刚刚测试了一些我确信会惨败的东西,但令我惊讶的是,它完美地工作,并向我自己证明我仍然对 async-await 的工作方式感到困惑。

我创建了一个线程,传递了一个 async void 委托作为线程的主体。 这是我的代码的过度简化:

var thread = new Thread( async () => {
   while( true ) {
      await SomeLengthyTask();
      ...
   }
});
thread.Start();
thread.Join();

据我所知,当执行遇到 await 关键字时,方法中有一个隐含的 return,在这种情况下是循环线程的主体,而其余代码包含在回调延续中。

由于这个事实,我很确定线程会在 await 产生执行后立即终止,但事实并非如此!

有人知道这个魔法是如何实现的吗?async 功能被剥离并且 async 同步等待还是有一些黑魔法由 CLR 完成,使其能够恢复因 await 而产生的线程?

线程确实很快就终止了。

但是由于 Thread 构造函数不接受 async lambda,所以你得到的是一个 async void 委托。

原始线程将结束,后续(await 之后的其余部分)将发布到 ThreadPool 并最终 运行 另一个线程。

您可以通过检查线程 ID 来测试:

var thread = new Thread(async () =>
{
    while (true)
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        await SomeLengthyTask();
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    }
});
thread.Start();
thread.Join();

Console.ReadLine();

输出:

3
5
5
...
5

为了使示例更简单,我们假设您有此 Run 方法:

void Run(Action action)
{
    action();
}

你用你的 async 代表

调用它
Run(async () => 
{
    while(true) 
    {
      await SomeLengthyTask();
      ...
    }
});

Run 的执行将在到达第一个 await 和 returns 时几乎立即完成。 async 委托的其余部分将在 ThreadPool 上与另一个线程继续。


通常,每次执行 async 方法时达到 await 时,线程就会丢失,并且继续(等待任务完成后的其余部分)将发布到 ThreadPool (除非存在 SynchronizationContext,例如在 UI 线程中)。它可能会在同一个线程上执行(如我的示例中的 5),但也可能不会。

在您的情况下,您明确创建的线程不是 ThreadPool 的一部分,因此它肯定会被终止,其余的将 运行 在不同的线程上。