4

I have public async method which calls 3 different APIs to get some data and then post the response to the next API. Now based on Stephen cleary's article here to avoid deadlock :

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

I wanted to know if same is true for private async methods? Do I need to use ConfigureAwait(false) when I am calling private async method as well? So something along the line

public async Task<int> ProcessAsync(ServiceTaskArgument arg)
{
    // do i need to use ConfigureAwait here while calling private async method?
    var response1 = await GetAPI1().ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    var response2= await PostAPI2(response1).ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await PostAPI3(response2).ConfigureAwait(false);

    return 1;
}

private async Task<string> GetAPI1()
{
    var httpResponse = await _httpClient.GetAsync("api1").ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await EnsureHttpResponseIsOk(httpResponse).ConfigureAwait(false);

    return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}

private async Task<string> PostAPI2(string data)
{
    var stringContent = new StringContent(data, Encoding.UTF8, "application/json");
    var httpResponse = await _httpClient.PostAsync("api2", stringContent).ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await EnsureHttpResponseIsOk(httpResponse).ConfigureAwait(false);

    return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}

private async Task<string> PostAPI3(string data)
{
    var stringContent = new StringContent(data, Encoding.UTF8, "application/json");
    var httpResponse = await _httpClient.PostAsync("api3", stringContent).ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await EnsureHttpResponseIsOk(httpResponse).ConfigureAwait(false);

    return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}

private async Task EnsureHttpResponseIsOk(HttpResponseMessage httpResponse)
{
    if (!httpResponse.IsSuccessStatusCode)
    {
        var content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
        throw new MyHttpClientException("Unexpected error has occurred while invoking http client.", content, httpResponse.Headers);
    }
}

Update1
Also I have gone through SO post here, but answer suggesting to use custom NoSynchronizationContextScope.
I wanted to know if I need to use ConfigureAwait(false) on private methods or not?

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
LP13
  • 30,567
  • 53
  • 217
  • 400

2 Answers2

7

I wanted to know if I need to use ConfigureAwait(false) on private methods or not?

As a general rule, yes. ConfigureAwait(false) should be used for every await unless the method needs its context.

However, if you use NoSynchronizationContextScope, then that removes the need for ConfigureAwait(false).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Reference implementation: http://stackoverflow.com/questions/28305968/use-task-run-in-synchronous-method-to-avoid-deadlock-waiting-on-async-method – Caramiriel Sep 15 '16 at 20:22
  • @Stephen Cleary do we still have to use `ConfigureAwait(false)` in `asp.net core` or is the deadlock issue only happens in classic asp.net – LP13 Feb 24 '20 at 19:31
  • @LP13: The deadlock only happens on classic ASP.NET. [On ASP.NET Core `ConfigureAwait(false)` has no effect](https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html). *However*, ASP.NET Core *also* fixed all the places where blocking was necessary. So blocking on asynchronous code still isn't *recommended*. – Stephen Cleary Feb 25 '20 at 20:50
  • I don't think `NoSynchronizationContextScope `is safe to use with async methods. If `await` is used inside the scope, and the continuation to that `await` runs on a random threadpool thread instead of the original, the `Dispose` will cause `SynchronizationContext.SetSynchronizationContext(_synchronizationContext)` to override the current sync context of that random thread! In a UI context, you would need an `await Thread.Yield()` afterwards to actually trigger a continuation back to the UI thread. – Ryan Nov 30 '20 at 07:27
  • 1
    @Ryan: `NoSynchronizationContextScope` can be used with `async` methods *as long as* it's used to *block* asynchronous methods. Using `await` instead of `Wait` within the `using` block would cause the problem you describe. For this reason, I've written a [`NoContext` method](https://github.com/StephenCleary/AsyncEx/blob/8a73d0467d40ca41f9f9cf827c7a35702243abb8/src/Nito.AsyncEx.Tasks/SynchronizationContextSwitcher.cs#L36-L56) that enforces correct usage. – Stephen Cleary Nov 30 '20 at 14:16
0

It depends on what your class is. Is it part of a class library? Or is it a web api or other service operation? As Stephen mentioned, it's safest to ConfigureAwait(false) to avoid deadlocks, but it can also add a lot of unnecessary code if you are absolutely sure the caller isn't blocking (.Wait() or .Result()) e.g., if ProcessAsync is a asp.net web api method.

Kevin
  • 965
  • 6
  • 12
  • 2
    If you really are sure the code doesn't need the context then adding the `ConfigureAwait` prevents scheduling every single continuation onto the synchronization context. Even if it doesn't *deadlock*, it'll impact performance, and also potentially delay the processing of operations that actually need that synchronization context. Particularly for library methods that are likely to be used a lot, with at least some of those uses being performance sensitive, this is worth doing. – Servy Sep 15 '16 at 20:44
  • @Servy nice addition. i'm sold :) – Kevin Sep 15 '16 at 20:46
  • @Servy Thanks. As suggested I will add ConfigureAwaite(false) . But I really wonder why this is not the default implementation in .net.? – LP13 Sep 15 '16 at 20:57
  • @LP13 Because pretty much everyone that's *not* developing a library wants to capture the context, and there's a lot more people writing line of business programs than there are people writing libraries. That, and the people writing libraries are typically going to know how to handle something like this more often than a LOB developer. – Servy Sep 15 '16 at 21:01