Task'的延续(由async/await构建)在WPF应用程序的主线程上运行,但在控制台应用程序的子线程上运行
本文关键字:线程 应用程序 运行 WPF 控制台 构建 延续 Task await async | 更新日期: 2023-09-27 18:07:03
假设我有一个简单的C#
控制台应用程序:
class Program
{
static async void func()
{
Thread.CurrentThread.Name = "main";
await Task.Run(() =>
{
Thread.CurrentThread.Name = "child";
Thread.Sleep(5000);
});
Console.WriteLine("continuation is running on {0} thread", Thread.CurrentThread.Name);
}
static void Main(string[] args)
{
func();
Thread.Sleep(10000);
}
}
当5000毫秒过去时,我们看到"continuation正在子线程上运行"消息。当另一个5000毫秒过去时,主线程完成它的工作并关闭应用程序。这看起来很符合逻辑:异步任务和它的延续在同一个子线程上运行。
但假设现在我有一个简单的WPF
应用程序:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
async private void mainWnd_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Thread.CurrentThread.Name = "main";
await Task.Run(() =>
{
Thread.CurrentThread.Name = "child";
Thread.Sleep(5000);
});
this.Title = string.Format("continuation is running on {0} thread", Thread.CurrentThread.Name);
}
private void mainWnd_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Thread.Sleep(10000);
}
}
现在,当我们按下鼠标左键并经过5000毫秒后,我们看到"continuation正在主线程上运行"标题。此外,如果我们按左键,然后按右键,应用程序将首先运行mainWnd_MouseLeftButtonDown
处理程序,然后运行mainWnd_MouseRightButtonDown
处理程序(在主线程上),主线程将休眠10000毫秒,然后从mainWnd_MouseLeftButtonDown
继续异步任务将在主线程上执行。
为什么async-await
机制在这两种情况下不同?
我知道在WPF
方法可以显式运行在UI线程通过Dispatcher.Invoke
,但async-await
机制不是WPF
特定的,所以它的行为应该是平等的,在任何类型的应用程序,不应该吗?
async-await
尊重当前作用域的SynchronizationContext
。这意味着在异步操作开始时捕获上下文(如果存在),并且在异步操作结束时在捕获的上下文上调度延续。
UI
应用程序(WPF
/Winforms
)使用SynchronizationContext
,只允许主(UI
)线程与UI
元素交互,因此它与async-await
无缝工作。
ASP.Net
也有自己的SynchronizationContext
,称为AspNetSynchronizationContext
(令人惊讶)。所以它不一定是关于UI
或单线程公寓。
如果你想禁用有用的SynchronizationContext
捕获,你只需要使用ConfigureAwait
:
await Task.Run(() =>
{
Thread.CurrentThread.Name = "child";
Thread.Sleep(5000);
}).ConfigureAwait(false);
更多关于SynchronizationContexts: It's All About the SynchronizationContext