I am studying C# Task, async/await and I have encountered a situation that I do not quite understand. I made two code snippet, the first one is based upon console application, and the second one is based upon wpf. Here are the two code snippet:
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);
}
}
Then, for console application, the result is:
for wpf, the result is:
My question is the third line of the two results. why these two behaves differently? As I understand, the thread ID in the third line and fourth line should be the same.
I hope if anyone could explain this question. Thank you.
In the WPF code in HeavyJob() you have this:
await Task.Delay(3000);
That does not have .ConfigureAwait(false) so for a WPF (or WinForms) application it will resume on the same thread that it was started on - that will be thread 1 in your example. It can do this because the SynchronizationContext has the ability to use the Windows message loop to resume on the UI thread.
Similarly in the console code in FooAsync() you also have an await without .ConfigureAwait(false). However, for console apps by default there is no SynchronizationContext available to use to resume on the calling thread. So in this case the await resumes on a new thread.