对 async 的语言支持允许 awaitables 控制自己的调度。当您等待任务时,会启动默认调度,但是您可以通过多种方式使用更多代码来更改默认行为。
具体来说, await 旨在执行以下操作:
- 测试可等待对象是否“完成”(GetAwaiter().IsCompleted)。
- 如果(且仅当)未完成可等待对象,请要求它安排方法的其余部分(GetAwaiter().OnCompleted(...))
- “实现”等待的结果。这意味着要么返回返回值,要么确保遇到的异常重新出现。
所以请记住,如果 awaitable 声称它已“完成”,那么什么都不会安排。
Task.Yield() 是新颖的,因为它返回了一个从未完成的等待对象,其明确目的是为您提供一种方法来显式地暂时停止执行,并立即安排其余的执行。它使用环境上下文,但还有许多其他方法可以在没有环境上下文的情况下进行类似的操作。
覆盖默认行为的一个示例是当您等待未完成的任务,但使用ConfigureAwait(false)方法时。ConfigureAwait(false) 将任务包装在一个特殊的 awaitable 中,它始终使用默认任务调度程序,有效地始终在线程池上恢复。'false' 用于显式忽略环境同步上下文。
没有 Task.Yield().ConfigureAwait(false),但考虑以下假设:
// ... A ...
await Task.Yield().ConfigureAwait(false);
// ... B ...
以上可以通过以下方式实现
// ... A ...
await Task.Run(() => {
// ... B ...
});
There is a little more explicitness and nesting there, but this isn't necessarily bad considering what's happening. Part 'A' always runs on the invoking thread, whereas part 'B' always runs on the thread pool. There are definitely differences on how you should look at code that is in sections A and B, so thus having some more curlies in between should hopefully get people to pause before assuming both sections are the same context.