Bailey Miller
Bailey Miller

Reputation: 1523

C# Async Await never returns to main thread

I wrote a really simple check that tells me what thread I am on and then added some async await code. I noticed that the first time I check I am on thread1, then thread3, and I never return to thread1 during my code execution.

Can anyone explain to me why after the await I don't return to the main thread that called await? The output of WhatThreadAmI() goes as follows:

**********************
Main - 17 -- True
**********************
**********************
CountAsync - 37 -- False
**********************
**********************
Main - 22 -- False
**********************
**********************
Main - 29 -- False
**********************

Example code:

class Program
{
    static Thread mainThread;
    public static async Task Main(string[] args)
    {
        mainThread = Thread.CurrentThread;


        WhatThreadAmI();

        Console.WriteLine("Counting until 100 million in 5 seconds ...");
        var msWait = await CountAsync();

        WhatThreadAmI();
        Console.WriteLine($"Counting to 100 million took {msWait} milliseconds.");


        Console.WriteLine("Press any key to exit");
        Console.ReadKey();

        WhatThreadAmI();
    }


    static async Task<String> CountAsync()
    {
        return await Task.Run(() =>
        {
            WhatThreadAmI();
            Task.Delay(TimeSpan.FromSeconds(5)).Wait();
            var startTime = DateTime.Now;

            var num = 0;

            while (num < 100000000)
            {
                num += 1;
            }

            return (DateTime.Now - startTime).TotalMilliseconds.ToString();

        });
    }


    static void WhatThreadAmI([CallerMemberName]string Method = "", [CallerLineNumber]int Line = 0)
    {
        const string dividor = "**********************";

        Debug.WriteLine(dividor);
        Debug.WriteLine($"{Method} - {Line} -- {IsMainThread()}");
        Debug.WriteLine(dividor);
    }

    public static bool IsMainThread() => mainThread == Thread.CurrentThread;

}

Upvotes: 3

Views: 3204

Answers (2)

Evk
Evk

Reputation: 101523

await will capture current synchronization context (SynchronizationContext.Current) and post continuation (everything after await) to that context (unless you use ConfigureAwait(false)). If there is no synchronization context, like in console application (your case) - by default continuation will be scheduled to thread pool thread. Your main thread is not thread pool thread, so you will never return to it in code you posted.

Note that every synhronization context implementation can decide what to do with callbacks posted to it, it does not necessary for it to post callback to single thread (like WPF\WinForms synchronization contexts do). So even with synchronization context you are not guaranteed to "return back to caller thread".

Upvotes: 3

Scott Chamberlain
Scott Chamberlain

Reputation: 127583

Even though you can now have async main functions there still no OperationContext for console applications. Because of that it will use the default task scheduler which will cause continuations after an await to use any thread pool thread instead of the original thread associated with the OperationContext.

Upvotes: 0

Related Questions