在不同线程上等待调用时会发生什么

What happens when awaiting a call on a different thread

假设我这样调用一个任务:

Task.Factory.StartNew(async () =>
                {
                    await SomeAction();
                }, TaskCreationOptions.LongRunning);

SomeAction里面有这样一行:

await OtherAction();

对于常规 await,代码返回到调用上下文,但这里是另一个线程上的任务 运行。那么 await 的上下文会发生什么呢?在这种情况下它真的像阻塞吗?

with a regular await, the code goes back to the invoking context, but here it's a task running on a different thread. So what happens with the context of the await there? Is it actually like blocking in this case?

Here's how await worksawait 从不阻塞。

await 将首先检查其可等待的(通常是任务);如果 awaitable 已经完成,那么 await 将提取结果(或异常)并继续(同步)执行该方法。如果等待 not 已经完成,then await 将异步运行。

await 行为异步时,它将(默认情况下)捕获当前“上下文”(SynchronizationContext.Current 或者,如果是 null,则 TaskScheduler.Current) ,然后 return 一个未完成的任务。由于示例代码使用 Task.Factory.StartNew 而未指定 TaskScheduler(IMO 始终是一个错误),因此它的委托在 TaskScheduler.Current 所在的任何位置执行 StartNew叫。因此 await 将捕获的上下文与 TaskScheduler.Current.

相同

如果 TaskScheduler.CurrentTaskScheduler.Default 相同(这很可能但绝不能保证),那么当 async 方法returns,线程returned到线程池,since StartNew doesn't understand async lambdas,从StartNewTaskreturned完成。这意味着那里的 LongRunning 标志只会让事情变得更慢;它与优化相反。

稍后,当 await 完成时,它将继续在捕获的上下文 (TaskScheduler.Current) 上执行 lambda。 如果 TaskScheduler.CurrentTaskScheduler.Default 相同,那么 lambda 将继续在从线程池中新获取的线程上执行(可能是同一个线程,但可能是不同的线程)一个)。


这听起来非常复杂,但实际上那只是因为代码使用了 StartNew。如果使用 Task.Run 查看代码,它会简单得多,因为上下文是 always TaskScheduler.Default(线程池上下文):

Task.Run(async () =>
{
  await SomeAction();
});

现在可以肯定地说,如果await是异步的,那么它会return一个任务,return把线程放到线程池中。当 SomeAction 完成时,lambda 将继续在线程池线程上执行。此外,由于 Task.Run 理解 async lambda,因此从 Task.Run 编辑的任务 return 在 lambda 完成之前不会完成。

这里有一些指南: