为什么异步/等待在UI线程上恢复?

时间:2019-02-20 12:42:17

标签: c# wpf asynchronous 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.
}

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

在UI线程上恢复使用“默认值”肯定会适得其反,因为有可能会阻塞UI,这正是async / await试图避免的情况。

在我的真实代码中,Method1做了大量的CPU绑定工作,因此为什么我使用Task.Run来调用它;该方法还调用许多异步I / O绑定方法。我使用async / await和Task.Run是否采取了正确的方法?

修改 为了澄清起见,我在Method1和Threads窗口中使用了断点,以查看代码在哪个线程上运行。在等待方法调用之前,“线程”窗口突出显示了工作线程,而在等待方法调用之后,突出显示了“主线程”。

Edit2 对于下面的@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

1 个答案:

答案 0 :(得分:1)

就像@ canton7在注释中指出的那样,我刚刚测试了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