【问题标题】:Why does async/await resume on the UI thread? [closed]为什么 async/await 在 UI 线程上恢复? [关闭]
【发布时间】:2019-02-20 22:19:18
【问题描述】:

我以为我理解 async/await 但显然不是。我有这样的事情:

public async void SomeEventHandler()
{
    await Task.Run(() => Method1());
}

private async Task Method1()
{
    // Running on a worker thread here.

    await SomeOtherMethodAsync();

    // Now running on the UI thread.
}

从上面的 cmets 中可以看出,Method1 一开始是在 b/g 线程上运行,正如您所期望的那样,它被使用 Task.Run() 调用。然而,在第一个await 之后,剩余的代码将在 UI 线程上运行。我相信将ConfigureAwait(false) 添加到“内部”异步方法调用将防止这种情况发生,但我不明白它首先在 UI 线程上恢复的原因。为什么是 UI 线程而不是原始(或其他)b/g 线程?

当然,“默认”在 UI 线程上恢复会适得其反,因为可能会阻塞 UI,而这正是 async/await 试图避免的。

在我的真实代码中,Method1 做了很多 CPU 密集型工作,因此我使用 Task.Run 来调用它;该方法还调用了许多异步 I/O 绑定方法。我在使用 async/await 和 Task.Run 时采取了正确的方法吗?

编辑 澄清一下,我在 Method1 和 Threads 窗口中使用了断点来查看代码在哪个线程上运行。在可等待方法调用之前,线程窗口突出显示了一个工作线程,而在可等待方法调用之后,“主线程”被突出显示。

编辑2 回应@Aly 下面的回答,使用相同的调试文本,这就是我所看到的:

ManagedThreadID (Dispatcher): 11
ManagedThreadID (MainThread - before Task.Run(Method1)): 11
ManagedThreadID (Method1 - before SomeOtherMethodAsync): 14
ManagedThreadID (SomeOtherMethodAsync): 14
ManagedThreadID (Method1 - after SomeOtherMethodAsync): 11
ManagedThreadID (MainThread - after Task.Run(Method1)): 18

【问题讨论】:

  • "现在在 UI 线程上运行。" ——不,不是。不可能。上面一行中的 await 没有 SynchronizationContext 来捕获。我刚刚在 WPF 应用程序中尝试过并确认了它。
  • @PanagiotisKanavos 不,这是不会发生的行为。有问题的await 没有要恢复的原始同步上下文。 SomeEventHandlerawait 之后的代码是另一回事,但这不是他要问的问题。请先尝试有问题​​的代码,然后再说出它执行 OP 所说的操作!
  • @PanagiotisKanavos 他说“方法 1 开始于您所期望的 b/g 线程上运行,已使用 Task.Run() 调用。但是在第一次等待之后,其余代码然后运行UI线程”这是相当明确的IMO。你说这是预期的行为,这是错误的。这得到了他示例中的代码 cmets 的支持。
  • @AndrewStephens 我认为你已经把你的问题简化得太多了,你最终得到的东西并不能证明你的问题。您断言代码处的注释“// 现在在 UI 线程上运行”。在 UI 线程上运行是不正确的,您可以通过一个简单的测试应用程序看到这一点。请使用实际重现您的问题的示例更新您的问题。
  • @AndrewStephens 对,所以问题出在我们看不到的代码中,您不会与我们分享。输出没有任何意义,因为它是某个包含问题的黑匣子的输出,但我们无法检查。如果您在您的问题 中发布了示例代码的输出,那将是不同的。但是,就目前而言,示例代码仍然无用,因为它没有说明问题。

标签: c# wpf asynchronous async-await


【解决方案1】:

就像 @canton7 在 cmets 中指出的那样,我刚刚测试了 WPF 应用程序上提供的代码,您描述的行为似乎并不实际存在。

我的测试:

public async void SomeEventHandler()
{
    Debug.WriteLine($"ManagedThreadID (Dispatcher): {Dispatcher.CurrentDispatcher.Thread.ManagedThreadId}");
    Debug.WriteLine($"ManagedThreadID (MainThread - before Task.Run(Method1)): {Thread.CurrentThread.ManagedThreadId}");
    await Task.Run(() => Method1());
    Debug.WriteLine($"ManagedThreadID (MainThread - after Task.Run(Method1)): {Thread.CurrentThread.ManagedThreadId}");
}
private static async Task Method1()
{
    Debug.WriteLine($"ManagedThreadID (Method1 - before SomeOtherMethodAsync): {Thread.CurrentThread.ManagedThreadId}");
    await SomeOtherMethodAsync();
    Debug.WriteLine($"ManagedThreadID (Method1 - after SomeOtherMethodAsync): {Thread.CurrentThread.ManagedThreadId}");
}
private static Task SomeOtherMethodAsync()
{
    Debug.WriteLine($"ManagedThreadID (SomeOtherMethodAsync): {Thread.CurrentThread.ManagedThreadId}");
    return Task.Delay(1000);
}

调试输出:

ManagedThreadID (Dispatcher): 1
ManagedThreadID (MainThread - before Task.Run(Method1)): 1
ManagedThreadID (Method1 - before SomeOtherMethodAsync): 3
ManagedThreadID (SomeOtherMethodAsync): 3
ManagedThreadID (Method1 - after SomeOtherMethodAsync): 3
ManagedThreadID (MainThread - after Task.Run(Method1)): 1

【讨论】:

  • 查看我的问题中的 Edit#2 以查看我的线程 ID 得到了什么...
猜你喜欢
  • 2021-01-04
  • 2019-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多