在不同线程上等待调用时会发生什么
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
works。 await
从不阻塞。
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.Current
与 TaskScheduler.Default
相同(这很可能但绝不能保证),那么当 async
方法returns,线程returned到线程池,since StartNew
doesn't understand async
lambdas,从StartNew
Task
returned完成。这意味着那里的 LongRunning
标志只会让事情变得更慢;它与优化相反。
稍后,当 await
完成时,它将继续在捕获的上下文 (TaskScheduler.Current
) 上执行 lambda。 如果 TaskScheduler.Current
与 TaskScheduler.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 完成之前不会完成。
这里有一些指南:
- NEVER, EVER, EVER use
Task.Factory.StartNew
without passing a TaskScheduler
。这是一个 100% 的时间,永远在线的规则。
- 对于异步代码,使用
Task.Run
而不是 Task.Factory.StartNew
。
假设我这样调用一个任务:
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
works。 await
从不阻塞。
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.Current
与 TaskScheduler.Default
相同(这很可能但绝不能保证),那么当 async
方法returns,线程returned到线程池,since StartNew
doesn't understand async
lambdas,从StartNew
Task
returned完成。这意味着那里的 LongRunning
标志只会让事情变得更慢;它与优化相反。
稍后,当 await
完成时,它将继续在捕获的上下文 (TaskScheduler.Current
) 上执行 lambda。 如果 TaskScheduler.Current
与 TaskScheduler.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 完成之前不会完成。
这里有一些指南:
- NEVER, EVER, EVER use
Task.Factory.StartNew
without passing aTaskScheduler
。这是一个 100% 的时间,永远在线的规则。 - 对于异步代码,使用
Task.Run
而不是Task.Factory.StartNew
。