gpt4 book ai didi

c# - 退出异步方法可以将控制权交还给不同的异步方法吗?

转载 作者:太空狗 更新时间:2023-10-29 23:38:50 27 4
gpt4 key购买 nike

我理解等待任务的异步将执行返回给调用者,允许它继续直到它需要结果。

在某一点之前,我对由此产生的想法的解释是正确的。看起来好像正在进行某种交错。我希望 Do3() 完成,然后将调用堆栈备份到 Do2()。查看结果。

        await this.Go();

哪个调用

        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
}

预期结果:

// 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

实际输出:

//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

这里到底发生了什么?

我正在使用 .NET 4.5 和一个简单的 WPF 应用来测试我的代码。

最佳答案

这是一个 WPF 应用程序,所有这些代码都在同一个 UI 线程上执行。代码中的每个 await 延续都是通过 DispatcherSynchronizationContext.Post 安排的,它将一条特殊的 Windows 消息发布到 UI 线程的消息队列。每个延续都按照其消息发布的顺序发生(这是特定于实现的,您不应该依赖它,但这就是它在这里的工作方式)。

因此,End Do3: Foo 的延续确实是在 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

那么,为什么 WPF 和 WinForms 之间存在差异,虽然两者都运行消息循环,而我们在这里只处理单线程代码?答案可以在这里找到:

Why a unique synchronization context for each Dispatcher.BeginInvoke callback?

WPF 的 DispatcherSynchronizationContext.Post 只是调用 Dispatcher.BeginInvoke,WPF 的一个重要实现细节是每个 Dispatcher.BeginInvoke 回调都在它自己独特的同步上下文,如链接问题中所述。

这会影响 Task 对象的 await 延续(如 await doFunc())。在 WinForms 中,此类延续是内联的(同步执行),因为 SynchronizationContext.Current 保持不变。在 WPF 中,它们不是内联的,而是通过 SynchronizationContext.Post 发布的,因为 SynchronizationContext.Current before await tasktask 完成后是不一样的(它在 task.GetAwaiter().OnCompleted 内部由 await 运行时基础结构代码进行比较).

因此,在 WPF 中,它通常是同一个 UI 线程,但与该线程相关联的同步上下文不同,因此延续可能会引发一个异步的 PostMessage 回调,由消息循环。除了 YieldAwaitable(由 Task.Yield 返回)之外,对于从 WPF UI 触发的 TaskCompletionSource.SetResult 样式延续,您还会遇到这种行为线。

这是相当复杂但特定于实现的东西。如果你想精确控制异步 await 延续的顺序,你可能想推出你自己的同步上下文,类似于 Stephen Toub 的 AsyncPump。 .尽管通常您不需要它,尤其是对于 UI 线程。

关于c# - 退出异步方法可以将控制权交还给不同的异步方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23785227/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com