Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I call ConfigureAwait(false) on every awaited operation

Tags:

People also ask

Should you always use ConfigureAwait false?

As a general rule, every piece of code that is not in a view model and/or that does not need to go back on the main thread should use ConfigureAwait false. This is simple, easy and can improve the performance of an application by freeing the UI thread for a little longer.

Which do I use ConfigureAwait True or false?

In this video we answer the ever popular question “Which do I use, ConfigureAwait True or False?”. The direct answer to this question is: – If you are a writing code for the UI, use ConfigureAwait(true).

Is it OK to use ConfigureAwait false only on the first await in my method and not on the rest?

Is it ok to use ConfigureAwait(false) only on the first await in my method and not on the rest? In general, no. See the previous FAQ. If the await task.

Is ConfigureAwait true default?

The exception here is app-level code, such as Windows Forms, WPF, and ASP.NET. ConfigureAwait(true) corresponds to the default behavior and does nothing meaningful, therefore such calls can be safely omitted.


I read this article https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html - however I'm seeing a contradiction:

I'm aware of the problem of deadlocking the UI thread because the UI thread blocks waiting for an async operation to complete, but the same async operation is synchronized to the UI thread context - consequently the async operation cannot enter the UI thread, so the UI thread won't stop waiting.

The article tells us the workaround is to not block on the UI thread, otherwise you need to use ConfigureAwait(false) everywhere:

You would have to use for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code.

However later on in the article the author writes:

Preventing the Deadlock
There are two best practices (both covered in my intro post) that avoid this situation:

  1. In your “library” async methods, use ConfigureAwait(false) wherever possible.
  2. Don’t block on Tasks; use async all the way down.

I'm seeing a contradiction here - in the "don't do this" section he writes that having to use ConfigureAwait(false) everywhere would be the consequence of blocking the UI thread - but in his "best practices" list he then tells us to do just that: "use ConfigureAwait(false) wherever possible." - though I suppose "wherever possible" would exclude third-party code, but in the case where there is no third-party code the result is the same if I block the UI thread or not.

As for my specific problem, here is my current code in a WPF MVVM project:

MainWindowViewModel.cs

private async void ButtonClickEventHandler()
{
    WebServiceResponse response = await this.client.PushDinglebopThroughGrumbo();

    this.DisplayResponseInUI( response );
}

WebServiceClient.cs

public class PlumbusWebServiceClient {

    private static readonly HttpClient _client = new HttpClient();

    public async Task<WebServiceResponse> PushDinglebopThroughGrumbo()
    {
        try
        {
            using( HttpResponseMessage response = await _client.GetAsync( ... ) )
            {
                if( !response.IsSuccessStatusCode ) return WebServiceResponse.FromStatusCode( response.StatusCode );

                using( Stream versionsFileStream = await response.Content.ReadAsStreamAsync() )
                using( StreamReader rdr = new StreamReader( versionsFileStream ) )
                {
                    return await WebServiceResponse.FromResponse( rdr );
                }
            }
        }
        catch( HttpResponseException ex )
        {
            return WebServiceResponse.FromException( ex );
        }
    }
}

If I understand the document correctly, I should add ConfigureAwait(false) to every await that is not in a method that has code that needs to run on the UI thread - which is every method inside my PushDinglebopThroughGrumbo method, but also all code in WebServiceResponse.FromResponse (which calls await StreamReader.ReadLineAsync). But what about any third-party code I call which also performs await operations on the StreamReader? I won't have access to their source-code so that would be impossible.

I'm also a bit put-off by having to place ConfigureAwait(false) everywhere - I thought the point of the await keyword was to eliminate explicit task library calls - shouldn't there be a different keyword for resume-context-free awaiting then? (e.g. awaitfree).

So should my code then look like this?

MainWindowViewModel.cs

(unmodified, same as above)

WebServiceClient.cs

public class PlumbusWebServiceClient {

    private static readonly HttpClient _client = new HttpClient();

    public async Task<WebServiceResponse> PushDinglebopThroughGrumbo()
    {
        try
        {
            using( HttpResponseMessage response = await _client.GetAsync( ... ).ConfigureAwait(false) ) // <-- here
            {
                if( !response.IsSuccessStatusCode ) return WebServiceResponse.FromStatusCode( response.StatusCode );

                using( Stream versionsFileStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false) )  // <-- and here
                using( StreamReader rdr = new StreamReader( versionsFileStream ) )
                {
                    return await WebServiceResponse.FromResponse( rdr ).ConfigureAwait(false);  // <-- and here again, and inside `FromResponse` too
                }
            }
        }
        catch( HttpResponseException ex )
        {
            return WebServiceResponse.FromException( ex );
        }
    }
}

...I would have thought that calling ConfigureAwait(false) would only be necessary on the topmost await call inside the PlumbusWebServiceClient method - i.e. the GetAsync call.

If I do need to apply it everywhere, could I simplify it to an extension method?

public static ConfiguredTaskAwaitable<T> CF<T>(this Task<T> task) {
    return task.ConfigureAwait(false);
}

using( HttpResponseMessage response = await _client.GetAsync( ... ).CF() )
{
    ...
}

...though this doesn't alleviate all of the fiddliness.

Update: Second example

Here is some async code I wrote that exports my application's settings to a simple text file - I can't help but think it doesn't feel right, is this really the correct way to do this?

class Settings
{
    public async Task Export(String fileName)
    {
        using( StreamWriter wtr = new StreamWriter( fileName, append: false ) )
        {
            await ExportSetting( wtr, nameof(this.DefaultStatus     ), this.DefaultStatus                         ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.ConnectionString  ), this.ConnectionString                      ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.TargetSystem      ), this.TargetSystem.ToString("G")            ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.ThemeBase         ), this.ThemeBase                             ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.ThemeAccent       ), this.ThemeAccent                           ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.ShowSettingsButton), this.ShowSettingsButton ? "true" : "false" ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.ShowActionsColumn ), this.ShowActionsColumn  ? "true" : "false" ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.LastNameFirst     ), this.LastNameFirst      ? "true" : "false" ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.TitleCaseCustomers), this.TitleCaseCustomers ? "true" : "false" ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.TitleCaseVehicles ), this.TitleCaseVehicles  ? "true" : "false" ).ConfigureAwait(false);
            await ExportSetting( wtr, nameof(this.CheckForUpdates   ), this.CheckForUpdates    ? "true" : "false" ).ConfigureAwait(false);
        }
    }

    private static async Task ExportSetting(TextWriter wtr, String name, String value)
    {
        String valueEnc = Uri.EscapeDataString( value ); // to encode line-breaks, etc.

        await wtr.WriteAsync( name ).ConfigureAwait(false);
        await wtr.WriteAsync( '=' ).ConfigureAwait(false);
        await wtr.WriteLineAsync( valueEnc ).ConfigureAwait(false);
    }
}