我正在研究C# Task、async/await,遇到了不太明白的情况。我制作了两个代码片段,第一个是基于控制台应用程序,第二个是基于wpf。这是两个代码片段:
async static Task Main()
{
Helper.PrintThreadId("Before");
await FooAsync();
Helper.PrintThreadId("After");
}
async static Task FooAsync()
{
Helper.PrintThreadId("Before");
await Task.Delay(1000);
Helper.PrintThreadId("After");
}
class Helper
{
private static int index = 1;
public static void PrintThreadId(string message = null, [CallerMemberName] string name = null)
{
var title = $"{index}: {name}";
if (!string.IsNullOrEmpty(message))
title += $" @ {message}";
Console.WriteLine("Thread ID: " + Environment.CurrentManagedThreadId + ", title: " + title);
Interlocked.Increment(ref index);
}
}
async Task<int> HeavyJob()
{
Helper.PrintThreadId("Before");
await Task.Delay(3000);
Helper.PrintThreadId("After");
return 10;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
Helper.PrintThreadId("Before");
var res = await HeavyJob().ConfigureAwait(false);
Helper.PrintThreadId("After");
}
class Helper
{
private static int index = 1;
public static void PrintThreadId(string message = null, [CallerMemberName] string name = null)
{
var title = $"{index}: {name}";
if (!string.IsNullOrEmpty(message))
title += $" @ {message}";
Debug.WriteLine("Thread ID: " + Environment.CurrentManagedThreadId + ", title: " + title);
Interlocked.Increment(ref index);
}
}
那么,对于控制台应用程序,结果是:
对于wpf,结果是:
我的问题是两个结果的第三行。为什么这两者的行为不同?据我了解,第三行和第四行的线程ID应该是相同的。
我希望有人能解释一下这个问题。谢谢。
在
HeavyJob()
中的 WPF 代码中,您有以下内容:
await Task.Delay(3000);
那没有有
.ConfigureAwait(false)
,所以对于WPF(或WinForms)应用程序来说,它将在启动它的同一线程上恢复 - 在您的示例中,这将是线程1。它可以做到这一点是因为 SynchronizationContext
能够使用 Windows 消息循环在 UI 线程上恢复。
类似地,在
FooAsync()
中的控制台代码中,您也有一个没有 .ConfigureAwait(false)
的等待。但是,对于控制台应用程序,默认情况下没有 SynchronizationContext
可用于在调用线程上恢复。因此,在这种情况下,等待将在新线程上恢复。