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

c#

假设我调用这样的任务:

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

里面SomeAction有这样一行:

await OtherAction();

使用常规await,代码返回到调用上下文,但这里是在不同线程上运行的任务。那么在那里等待的上下文会发生什么?在这种情况下,它实际上就像阻塞一样吗?

回答

使用常规等待,代码返回到调用上下文,但这里是在不同线程上运行的任务。那么在那里等待的上下文会发生什么?在这种情况下,它实际上就像阻塞一样吗?

这是如何await工作的。await从不阻塞。

await将首先检查其等待(通常是一个任务);如果 awaitable 已经完成,await则将提取结果(或异常)并继续执行该方法(同步)。如果awaitable是不是已经完成,那么 await将表现异步。

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

如果 TaskScheduler.CurrentTaskScheduler.Default(这很可能但绝不保证)相同,那么当async方法返回时,线程返回到线程池,并且由于StartNew不理解asynclambdas,Task返回的 fromStartNew已完成。这意味着LongRunning那里的标志只会让事情变慢;它与优化相反。

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


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

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

现在你可以肯定地说,如果await行为是异步的,那么它会返回一个任务,将线程返回到线程池中。当SomeAction完成,那么拉姆达将继续在一个线程池线程执行。此外,由于Task.Run了解asynclambdas,从 lambda 返回的任务Task.Run将不会完成,直到 lambda 完成。

以下是一些指导方针:

  • 永远,永远,永远不要在Task.Factory.StartNew不通过TaskScheduler. 这是 100% 的时间、永远在线的规则。
  • 对于异步代码,请使用Task.Run代替Task.Factory.StartNew

以上是等待不同线程上的调用时会发生什么的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>