56

Has anyone come across a scenario for using ConfigureAwait(true)? Since true is the default option I cannot see when would you ever use it.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61

5 Answers5

25

true to attempt to marshal the continuation back to the original context captured; otherwise, false.

It's actually more like saying that ConfigureAwait(true) is like using .ContinueWith( t => {...}, TaskScheduler.FromCurrentSynchronizationContext()), where ConfigureAwait(false) is like using .ContinueWith( t => {...}). If you pass false, then the continuation is being allowed to run on a thread-pool thread instead of pulling back to the current synchronization context.

Yanshof
  • 9,659
  • 21
  • 95
  • 195
  • 5
    Nice explanation, although this only explains what `ConfigureAwait(true)` does, but doesn't explain **when** someone would **want** to do it. It would be nice to know some examples where someone would want `.ConfigureAwait(true)`. Is it the same as not calling `.ConfigureAwait()` at all, or avoiding the use of `.ConfigureAwait()` will make the code randomly use or not use current context? Why would someone need Synchronization Context in an ASP.NET MVC app, for example? – Alisson Reinaldo Silva May 19 '17 at 03:57
  • 1
    To know more about what a SynchronizationContext is and etc, see [What does SynchronizationContext do](https://stackoverflow.com/questions/18097471/what-does-synchronizationcontext-do) – Majid Mar 29 '18 at 16:20
12

One possibility I can see is if you're writing code in a library and you want to allow your callers to decide whether it's appropriate to continue on the original context1 (although I'd usually argue for never continuing on the original context from within library code)

Your caller will either pass a bool parameter or set some configuration value, and so you will not know until runtime what the correct argument value is.


This is the general type of answer for APIs such as this that have a no-args variant and a variant with a single argument, where the no-args variant is documented as "the same as the single argument variant with well-known value x" - if you won't know until runtime what the correct value to pass is, then you should just call the single argument variant with the correct runtime value.


1 E.g. your caller is also supplying a delegate. Your caller will know (and can decide) whether that delegate needs to be back on the original context or not.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • 2
    This answer perfectly describes the approach that the [Polly](https://github.com/App-vNext/Polly/) library [later took](https://github.com/App-vNext/Polly#synchronizationcontext). A real-world example of why a caller might want to enforce continuing on the original context, `.ConfigureAwait(true)`, is [if they are using a custom TaskScheduler](https://github.com/App-vNext/Polly/issues/49). – mountain traveller Apr 01 '17 at 19:15
  • One exception - thread static access like DbCommandInterceptor patterns – jjxtra Nov 20 '19 at 19:23
  • 3
    This answer suggests calling `ConfigureAwait(boolVariable)`, which is good. But the original question is whether there is every a reason to do `ConfigureAwait(true)`, and it sounds like the answer is no. – BVernon Mar 27 '20 at 21:06
  • "although I'd usually argue for never continuing on the original context from within library code" Why? – John Mar 20 '23 at 14:13
  • @John - because generally you don't know how expensive asking to be returned to the original context will be nor can you make use of any specific features of whatever context you're returning to. All the (possible) cost, none of the (presumed) benefits. – Damien_The_Unbeliever Mar 20 '23 at 14:27
  • @Damien_The_Unbeliever That makes sense. However, when a library consistently returns to your context it has the benefit of the user no needing to take care of this issue. I guess it depends a bit on what the library is for and whether the efficiency thing could potentially matter or not. – John Mar 22 '23 at 12:19
  • @John - but by default, the consumer of the library in most cases will be writing their own `await`s in their own methods and *those* awaits can have the "return to context or not decision" made directly by the consumer. What happens inside the library code doesn't affect the consumer's decisions. – Damien_The_Unbeliever Mar 22 '23 at 12:35
  • @Damien_The_Unbeliever MudBlazor has a `Task ShowMessageBox(...)` method that waits until the user has closed the box. It would be pretty weird if I had to decorate all those with `ConfigureAwait`s. It would be equally unreasonable to have to wrap all such methods. – John Mar 22 '23 at 16:38
6

Since true is the default option I cannot see when would you ever use it.

One obvious use case is when you want to make sure that every time something is awaited, there is made an explicit and deliberate choise about what to do with the synchronization context.

Example policy from http://newmedialabs.co.za/blog/post/SynchronizationContexts:

At NML we prefer to always state explicitly how we want the task continuation to occur. So even though a Task's default is ConfigureAwait(true), we still specify it as such so that we are always cognizant of what's happening "under the hood".

Although to improve readability they use an extension instead of ConfigureAwait(true) directly:

However, when you look at a lot of code, some with ConfigureAwait(true) and some with ConfigureAwait(false), it's not easy to spot where they differ. So we use either ConfigureAwait(false), or a useful extension method, ContinueOnCapturedContext(). It does the same thing, but just differentiates it from ConfigureAwait(false) in a more visual manner.

hlovdal
  • 26,565
  • 10
  • 94
  • 165
  • 2
    This is also echoed by [Eli Arbel](https://arbel.net/2014/09/21/configureawait-or-not/): `I’ve recently come to the conclusion that it’s best to always specify ConfigureAwait everywhere lest you forget.`, which wrote a Resharper extention to adress this. – hlovdal Apr 13 '16 at 11:35
  • 1
    The link to on [newmedialabs.co.za](http://newmedialabs.co.za/blog/post/SynchronizationContexts) is no more available. But I managed to save (on GitHub) [the essential information regarding to synchronization context, tasks continuation and the handy extension method](https://github.com/VeaceslavWD/EasySharp/blob/master/NHelpers/CustomExtensionMethods/TaskHelper.cs) ripped from that blog. – AlexMelw Jul 16 '17 at 15:07
  • 1
    this is the only answer that answers the question 'why use it since true is the default' – fotisgpap Mar 17 '18 at 07:14
  • 2
    I think there should not be a default and the caller should specify. A lot of people don't realise that this even exists and what its implications are. – rollsch Mar 26 '18 at 03:39
5

A situation to use ConfigureAwait(true) is when performing await in a lock, or using any other context/thread specific resources. This requires a synchronization context, which you will have to create, unless you are using Windows Forms or WPF, which automatically create a UI synchronization context.

In the following code (assumed to be called from the UI thread and synchronization context), if ConfigureAwait(false) was used, the locks would attempt to release on a different thread, causing an exception. This is a simple example that updates a large config file if it has changed from an external source, attempting to avoid write disk IO if the config file is the same as before.

Example:

/// <summary>
/// Write a new config file
/// </summary>
/// <param name="xml">Xml of the new config file</param>
/// <returns>Task</returns>
public async Task UpdateConfig(string xml)
{
    // Ensure valid xml before writing the file
    XmlDocument doc = new XmlDocument();
    using (XmlReader xmlReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { CheckCharacters = false }))
    {
        doc.Load(xmlReader);
    }
    // ReaderWriterLock
    configLock.AcquireReaderLock(Timeout.Infinite);
    try
    {
        string text = await File.ReadAllTextAsync(ConfigFilePath).ConfigureAwait(true);

        // if the file changed, update it
        if (text != xml)
        {
            LockCookie cookie = configLock.UpgradeToWriterLock(Timeout.Infinite);
            try
            {
                // compare again in case text was updated before write lock was acquired
                if (text != xml)
                {
                    await File.WriteAllTextAsync(ConfigFilePath, xml).ConfigureAwait(true);
                }
            }
            finally
            {
                configLock.DowngradeFromWriterLock(ref cookie);
            }
        }
    }
    finally
    {
        configLock.ReleaseReaderLock();
    }
}
jjxtra
  • 20,415
  • 16
  • 100
  • 140
4

If you are using Azure's Durable Functions, then you must use ConfigureAwait(true) when awaiting your Activity functions:

string capture = await context.CallActivityAsync<string>("GetCapture", captureId).ConfigureAwait(true);

Otherwise you will likely get the error:

"Multithreaded execution was detected. This can happen if the orchestrator function code awaits on a task that was not created by a DurableOrchestrationContext method. More details can be found in this article https://learn.microsoft.com/en-us/azure/azure-functions/durable-functions-checkpointing-and-replay#orchestrator-code-constraints.".

Further information can be found here.

ItsPete
  • 2,363
  • 3
  • 27
  • 35
pirsqua
  • 346
  • 1
  • 2
  • 12
  • 2
    I don't think he was asking when you would want the value to be true, but when you would actually want to specify it since it's already the default. – BVernon Mar 27 '20 at 21:09