Vin*_*cio 5 c# asynchronous async-await
我理解等待任务的异步会将执行返回给调用者,允许它继续直到需要结果.
我对我的想法的解释是正确的,直到某一点为止.看起来似乎正在进行某种交错.我希望Do3()完成,然后将调用堆栈备份到Do2().查看结果.
await this.Go();
Run Code Online (Sandbox Code Playgroud)
哪个电话
async Task Go()
{
await Do1(async () => await Do2("Foo"));
Debug.WriteLine("Completed async work");
}
async Task Do1(Func<Task> doFunc)
{
Debug.WriteLine("Start Do1");
var t = Do2("Bar");
await doFunc();
await t;
}
async Task Do2(string id)
{
Debug.WriteLine("Start Do2: " + id);
await Task.Yield();
await Do3(id);
Debug.WriteLine("End Do2: " + id);
}
async Task Do3(string id)
{
Debug.WriteLine("Start Do3: " + id);
await Task.Yield();
Debug.WriteLine("End Do3: " + id); // I did not expect Do2 to execute here once the method call for Do3() ended
}
Run Code Online (Sandbox Code Playgroud)
预期结果:
// Start Do1
// Start Do2: Bar
// Start Do2: Foo
// Start Do3: Bar
// Start Do3: Foo
// End Do3: Bar
// End Do2: Bar
// End Do3: Foo
// End Do2: Foo
//Completed async work
Run Code Online (Sandbox Code Playgroud)
实际产量:
//Start Do1
//Start Do2: Bar
//Start Do2: Foo
//Start Do3: Bar
//Start Do3: Foo
//End Do3: Bar
//End Do3: Foo
//End Do2: Bar
//End Do2: Foo
//Completed async work
Run Code Online (Sandbox Code Playgroud)
这到底发生了什么?
我正在使用.NET 4.5和一个简单的WPF应用来测试我的代码.
这是一个 WPF 应用程序,所有这些代码都在同一个 UI 线程上执行。await代码中的每个延续都是通过安排的DispatcherSynchronizationContext.Post延续都是通过 进行调度的,它将一条特殊的 Windows 消息发布到 UI 线程的消息队列。每个延续都按照其消息发布的顺序发生(这是特定于实现的,您不应该依赖于此,但这就是它在这里的工作方式)。
因此, for 的延续End Do3: Foo确实是在 for 的延续之后发布的End Do3: Bar。输出是正确的。
现在,更多细节。当我询问 WinForms 与 WPF 时,我希望您的“预期”输出与实际输出相匹配。我刚刚在WinForms下测试了它,它确实匹配:
// Start Do1
// Start Do2: Bar
// Start Do2: Foo
// Start Do3: Bar
// Start Do3: Foo
// End Do3: Bar
// End Do2: Bar
// End Do3: Foo
// End Do2: Foo
//Completed async work
Run Code Online (Sandbox Code Playgroud)
那么,为什么 WPF 和 WinForms 之间存在差异,两者都运行消息循环,而我们在这里只处理单线程代码呢?答案可以在这里找到:
为什么每个 Dispatcher.BeginInvoke 回调都有唯一的同步上下文?
WPFDispatcherSynchronizationContext.Post只是调用Dispatcher.BeginInvoke,WPF 的一个重要实现细节是每个Dispatcher.BeginInvoke回调都在其自己唯一的同步上下文上执行,如链接问题中所述。
这会影响对象await的延续Task(例如await doFunc())。在 WinForms 中,此类延续是内联的(同步执行),因为SynchronizationContext.Current保持不变。在 WPF 中,它们不是内联的,而是通过 发布的SynchronizationContext.Post,因为完成SynchronizationContext.Current 之前 和之后是不一样的(它由运行时基础结构代码在内部进行比较)。await tasktasktask.GetAwaiter().OnCompletedawait
PostMessage因此,在 WPF 中,它通常是相同的 UI 线程,但与该线程关联的同步上下文不同,因此延续可能会导致消息循环发布、泵送和执行另一个异步回调。此外YieldAwaitable(由Task.Yield)返回,您还会体验到这种行为TaskCompletionSource.SetResult从 WPF UI 线程触发的 -style 延续的这种行为。
这是相当复杂但特定于实现的东西。如果您想精确控制异步await延续的顺序,您可能需要推出您自己的同步上下文,类似于 Stephen Toub 的AsyncPump。尽管通常您不需要它,特别是对于 UI 线程。
| 归档时间: |
|
| 查看次数: |
191 次 |
| 最近记录: |