c#task

Why these two C# code blocks about Task behaves differently?


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:

  1. code for console application:
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);
    }
}
  1. code for wpf (Helper is exactly same as in console application):
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:

![enter image description here

for wpf, the result is:

![enter image description here

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.


Solution


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.