I was expecting to get back to Thread#1 at location 1.2, but I did not. Is there a way to get back to UI thread after making the async call? Thanks

Also I cannot make the top level method async. Not sure if async all the way will solve this issue but I don't have that choice right now.

class Program
{
    static void Main(string[] args)
    {
        ComputeThenUpdateUI().Wait();
    } 
    static async Task ComputeThenUpdateUI()
    {
        Console.WriteLine($"1.1 {Thread.CurrentThread.ManagedThreadId}");
        await ExpensiveComputingNonUI().ConfigureAwait(true);
        Console.WriteLine($"1.2 {Thread.CurrentThread.ManagedThreadId}");
    } 
    static async Task ExpensiveComputingNonUI()
    {
        Console.WriteLine($"2.1 {Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(3000).ConfigureAwait(false);
        Console.WriteLine($"2.2 {Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(3000).ConfigureAwait(false);
        Console.WriteLine($"2.3 {Thread.CurrentThread.ManagedThreadId}");
    }
}
 
 
Output:
1.1 1
2.1 1
2.2 4
2.3 4
1.2 4

2

There are 2 best solutions below

3
On

Re : Doesn't .ConfigureAwait(true) mean that flow will return to the same thread once the await completes?

No, in most cases, there is no guarantee. Stephen Cleary has a definitive answer:

This "context" is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current. (If there is no currently-running task, then TaskScheduler.Current is the same as TaskScheduler.Default, the thread pool task scheduler).

It's important to note that a SynchronizationContext or TaskScheduler does not necessarily imply a particular thread. A UI SynchronizationContext will schedule work to the UI thread; but the ASP.NET SynchronizationContext will not schedule work to a particular thread.

i.e. only calls made from the UI thread of e.g. WPF or Windows Forms are guaranteed to return to the same thread. If you really need to return to the same thread, then you'd need to do customization to ensure the continuation is scheduled on the original thread.

Unless this is essential (such as for UI threads), returning to the same thread is generally undesirable as this can reduce performance (if there is contention for the thread) and lead to deadlocks

Re: Cannot make the top level main method async

This is now possible in C#7.1:

public static async Task Main(string[] args)
{
    // Can await asynchronous tasks here.
}
0
On

ConfigureAwait is a local decision, within each method that you write. ConfigureAwait(true) is a way of indicating that you want to try to return to the same context in which your method was running when it was first entered.

Firstly, this means that if you don't know what context you were originally running in then there's not a great deal of point in trying to get back to that same context. You shouldn't be trying to do anything that is context-dependent if you can be called in a variety of contexts. This is why it's usually good advice to use ConfigureAwait(false) if you're authoring a library. You will not know the contexts in which your code is called.

And secondly, it means that there is no "global" context to return to. If your caller (or any of it's callers, etc, higher up the chain) have already separated themselves from their original calling context, there's no way (via this mechanism) to return to that context.

If you want to switch to a specific context (such as a UI thread) and that's not guaranteed to be your calling context, you need to use a different, context-specific mechanism, such as Dispatcher.Invoke() or whatever is appropriate in your environment.