vico
vico

Reputation: 18171

Understanding ConfigureAwait

Trying to understand when I should use ConfigureAwait().
According to book:

When an async method resumes after an await, by default it will resume executing within the same context. This can cause performance problems if that context was a UI context and a large number of async methods are resuming on the UI context.

Solution

To avoid resuming on a context, await the result of ConfigureAwait() and pass false for its continueOnCapturedContext parameter:

async Task ResumeWithoutContextAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);

    //This method discards its context when it resumes.
}

What is context and how to see ConfigureAwait() changes things in sample application:

static async Task ResumeWithoutContextAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(true);
    Console.WriteLine("ManagedThreadId {0}", Thread.CurrentThread.ManagedThreadId);

    //This method discards its context when it resumes.
}

static void Main(string[] args)
{
    ResumeWithoutContextAsync();

    Console.ReadLine();
}

I was thinking that context is Thread, but it is not.

Upvotes: 5

Views: 4394

Answers (2)

Rick O'Shea
Rick O'Shea

Reputation: 1449

Today we mostly write JSON services in fast services accessed via REST calls. Native GUI applications and also IIS Application Server require async tasks resume execution on the original thread context. Unfortunately, that bias toward last century development forces us to use "ConfigureAwait(false)" on virtually every call in our service code.

Finally it's a really confusing and misleading name. It reads as "do or do not configure await" but in fact it is "thread affinity true or false". There are no other options so it's NOT a general configuration method.

Upvotes: -2

Evk
Evk

Reputation: 101493

Context here is SynchronizationContext. Await will post continuation (the rest of the method after await) to current context (SynchronizationContext.Current) if it's present and if you did not use ConfigureAwait false. If continuation is not posted to context - it will be executed on thread pool thread. In console applications there is no synhronization context by default, so in your test ConfigureAwait has no effect. You can create dummy context to see the effect though:

class MySynchornizationContext : SynchronizationContext {
    public override void Post(SendOrPostCallback d, object state) {
        Console.WriteLine("posted");
        base.Post(d, state);
    }

    public override void Send(SendOrPostCallback d, object state) {
        Console.WriteLine("sent");
        base.Send(d, state);
    }
}

Then in the beginning of Main method:

SynchronizationContext.SetSynchronizationContext(new MySynchornizationContext());

With ConfigureAwait(true) (or without at all) - you will see that continuation was posted to context ("posted" line in console). With ConfigureAwait(false) - you will see it is not.

Real synchronization contexts are more complicated than that of course. For example UI context (like in winforms or WPF) will "queue" continuations and execute them on one (UI) thread. That might be problematic as described in your question quote, for various reasons (and may lead to deadlocks), so when you are writing general-purpose library - it's benefical to use ConfigureAwait(false to avoid this behavior.

SynchronizationContext is not required to post all callbacks to a single thread of course, it can do anything with them. For example ASP.NET MVC context (old versions at least) will post callbacks to the request thread, and there can be many requests so many request threads.

Upvotes: 5

Related Questions