让我们假设我有以下代码:
await Task1Async().ConfigureAwait(false);
await Task2ASync().ConfigureAwait(false);
配置 await 允许在与调用线程不同的线程上运行。
但是如果我执行以下操作:
Task.WaitAll(
Task1Async,
Task2Async
);
- 任务是否在同一上下文中运行?
- 如果没有,我可以做一个等效的配置等待(假(吗?
async Task TestAsync()
{
await Task1Async().ConfigureAwait(false);
await Task2ASync().ConfigureAwait(false);
}
配置 await 允许在与调用线程不同的线程上运行。
澄清一下,代码示例中的ConfigureAwait(false)
对Task1Async
和Task2Async
中的代码绝对没有影响。默认情况下,await
将捕获其上下文并在该上下文上恢复;这适用于await
所在的方法 -TestAsync
上面的代码。有关详细信息,请参阅我的异步介绍。
但是,如果我[使用
Task.WaitAll
],任务是否在同一上下文中运行?
Task.WaitAll
用于等待任务。它不运行任务。TAP 模式指定任务返回"热",即已在进行中。因此,当您的代码调用Task1Async()
并取回任务时,该任务已在进行中。代码获取任务后,告诉它在哪里运行为时已晚 - 它已经开始了。
如果没有,我可以做一个等效的配置等待(假(吗?
如果你的意思是,"我可以在后台线程上运行这段代码吗?",那么你可以使用Task.Run
// Start Task1Async on a background thread.
var task1 = Task.Run(() => Task1Async());
// Start Task2Async on a background thread.
var task2 = Task.Run(() => Task2Async());
// Asynchronously wait for both tasks to complete.
await Task.WhenAll(task1, task2);
据我了解的一些解释。
如果您这样做:
//some code
await Task1Async();
await Task2ASync();
//some other code
它将像这样执行:
- 调用线程(例如 UI 上下文(同步执行"某些代码"。
- 然后,它异步启动 Task1Async,这将发生在 ThreadPool 上下文中(除非它已经完成,在这种情况下,执行在下面的第 4 点同步继续(。 如果任务未完成,则在此处捕获调用上下文
- 然后,当前线程从函数返回,这意味着它不会被阻塞。
- 当 Task1Async 完成后,下一行将在捕获的上下文中执行,例如 UI 上下文。 然后,这将通过线程池上下文异步启动 Task2Async(如果已完成,则继续同步执行(
- Task2Async 完成,然后最后使用捕获的上下文再次运行"其他代码">
如果您改为这样做:
//some code
await Task1Async().ConfigureAwait(false);
await Task2ASync().ConfigureAwait(false);
//some other code
这是发生的情况:
1.调用线程执行"一些代码"。
2. 然后异步执行 Task1Async(线程池(。
3. 当前线程从函数返回,这意味着它没有被阻塞。 4. 当 Task1Async 完成时,下一行不是在捕获的上下文上执行,而是使用线程池执行。 然后启动 Task2Async。 5. Task2Async 完成,然后再次运行最终的"其他代码",而不是在调用线程上,而是使用线程池。
在这两种情况下,函数都按顺序运行,但不会阻塞调用线程。
使用 Task.WaitAll,这两个任务使用线程池按顺序运行。 即每个任务在单独的线程中启动并等待完成。 这里的主要区别在于调用线程被阻塞,直到两个任务都完成。
如果要并行运行这两个任务并且不阻止调用线程,则可以等待 WhenAll:
await Task.WhenAll(Task1Async, Task2Async);
最后,如果您在 await 之后有任何想要运行的代码不需要在 UI 上下文中运行,那么您可以执行以下操作:
await Task.WhenAll(Task1Async, Task2Async).ConfigureAwait(false);
旁注:您想要 ConfigureAwait(true( 或省略它的原因是,如果您有一个按钮单击处理程序并希望更新需要在 UI 线程上的内容:
public void OnClick(..args..)
{
Button.IsEnabled = false;
await SomeTask();
//must be on UI thread. This code is executed on the context captured by the await above.
Button.IsEnabled = true;
}