rit*_*gig 10 c# wpf multithreading task async-await
我以为我了解异步等待模式和Task.Run
操作。
但我想知道为什么在下面的代码示例中,await
从完成的任务返回后不同步回 UI 线程。
public async Task InitializeAsync()
{
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}"); // "Thread: 1"
double value = await Task.Run(() =>
{
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}"); // Thread: 6
// Do some CPU expensive stuff
double x = 42;
for (int i = 0; i < 100000000; i++)
{
x += i - Math.PI;
}
return x;
}).ConfigureAwait(true);
Console.WriteLine($"Result: {value}");
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}"); // Thread: 6 - WHY??
}
Run Code Online (Sandbox Code Playgroud)
此代码在带有附加 Visual Studio 2019 调试器的 Windows 10 系统上的 .NET Framework WPF 应用程序中运行。
我正在从我的App
类的构造函数中调用此代码。
public App()
{
this.InitializeAsync().ConfigureAwait(true);
}
Run Code Online (Sandbox Code Playgroud)
也许这不是最好的方法,但我不确定这是否是奇怪行为的原因。
代码从 UI 线程开始,应该执行一些任务。随着await
操作和ConfigureAwait(true)
任务完成后,它应该继续在主线程 (1) 上。但事实并非如此。
为什么?
Vla*_*lad 12
这是一件棘手的事情。
您正在调用await
UI 线程,这是真的。但!您正在App
的构造函数中执行此操作。
请记住,隐式生成的启动代码如下所示:
public static void Main()
{
var app = new YourNamespace.App();
app.InitializeComponent();
app.Run();
}
Run Code Online (Sandbox Code Playgroud)
用于返回主线程的事件循环仅作为Run
执行的一部分启动。所以在App
构造函数运行期间,没有事件循环。然而。
因此,对SynchronizationContext
,这对流动后的主线程返回技术负责await
,是null
在应用程序的构造。
(在等待之前SynchronizationContext
被捕获,所以在完成之后已经有一个有效的没有关系:捕获的值是,所以继续在线程池线程上执行。)await
Task
SynchronizationContext
null
await
所以问题不在于您在构造函数中运行代码,问题在于您在 的构造函数中运行它App
,此时应用程序尚未完全设置好以供执行。MainWindow
的构造函数中的相同代码将表现良好。
让我们做一些实验:
public App()
{
Console.WriteLine($"sc = {SynchronizationContext.Current?.ToString() ?? "null"}");
}
protected override void OnStartup(StartupEventArgs e)
{
Console.WriteLine($"sc = {SynchronizationContext.Current?.ToString() ?? "null"}");
base.OnStartup(e);
}
Run Code Online (Sandbox Code Playgroud)
第一个输出给出
sc = null
Run Code Online (Sandbox Code Playgroud)
第二
sc = System.Windows.Threading.DispatcherSynchronizationContext
Run Code Online (Sandbox Code Playgroud)
所以你可以看到已经OnStartup
有一个同步上下文。因此,如果您InitializeAsync()
进入OnStartup
,它将按照您的预期运行。