5

I came across articles below regarding when and where to use ConfigureAwait(false), but cannot get an answer.

You Don’t Need ConfigureAwait(false), But Still Use It in Libraries, and UI apps. (e.g. Xamarin, WinForms etc)

https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html

This link says opposite answer

Best practice to call ConfigureAwait for all server-side code

When correctly use Task.Run and when just async-await

My questions:

Scenario 1: The code below is running as background service.

My question: Is ConfigureAwait(false) required for whenever await is used like both A and B below:

    [Service(Name = "com.MainApplicationService", Label = "Main Application Service", Exported = false)]
    public class MainApplicationService : Android.App.Service
    {

        public override IBinder OnBind(Intent intent)
        {
            return null;
        }

        [return: GeneratedEnum]
        public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
        {
            await InitAsync().ConfigureAwait(false);   //line A

            Task.Run(async () => await InitAsync().ConfigureAwait(false));   //line B

            return StartCommandResult.Sticky;
        }
    }

Scenario 2: The code below is running as UI thread as opposed to background service

Same question: Is ConfigureAwait(false) required for whenever await is used like both C and D below:

public class StartupActivity : Android.App.Activity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        await InitAsync().ConfigureAwait(false);  //line C

        Task.Run(async () => await InitAsync().ConfigureAwait(false));  //line D

        Finish();
    }
}

Xamarin Android ver 8, I think it is .net standard.

https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md

Pingpong
  • 7,681
  • 21
  • 83
  • 209
  • Are you missing `await` here: `Task.Run(async () => await InitAsync().ConfigureAwait(false)); //line B` ? – noseratio Nov 22 '19 at 22:01
  • Are you missing `async` modifier on `OnStartCommand` and `OnCreate` methods? – noseratio Nov 22 '19 at 22:02
  • yes, that is another issue that I need to find out. Do you know the answer? – Pingpong Nov 22 '19 at 22:03
  • 1
    I've shared my thoughts on that. Regarding using async APIs from async or non-async `void` methods like in your case, see https://stackoverflow.com/q/22629951/1768303 and linked Q/As. – noseratio Nov 22 '19 at 22:29
  • @noseratio Thanks for the link. – Pingpong Nov 22 '19 at 23:17
  • Double check: use discard like this `_ = WorkAsync();` to suppress warning, but it doesn't catch exception. To handle exception, I need to define an extension method like `Forget. ` on https://stackoverflow.com/questions/22864367/fire-and-forget-approach/22864616#22864616 ??? – Pingpong Nov 22 '19 at 23:27
  • I've edited my answer to address your comments. – noseratio Nov 23 '19 at 00:25

2 Answers2

3

Perhaps an unpopular opinion, but these days I don't use ConfigureAwait(false) even in libraries, see:

"Revisiting Task.ConfigureAwait(continueOnCapturedContext: false)"

IMO, if the code that consumes a Task-based API is concerned about the current synchronization context and how it might affect that API's behavior (deadlocks, redundant context switches, etc.), it can explicitly wrap the API invocation with Task.Run or use something like TaskExt.WithNoContext from the above link:

await Task.Run(() => InitAsync());
// or
await TaskExt.WithNoContext(() => InitAsync());

In most cases though, especially for UI apps (where there's a synchronization context, but threading scalability is not an issue), it's OK to leave it as is, without Task.Run or ConfigureAwait:

await InitAsync();

This would give you a chance to discover and investigate potential deadlocks, before trying to mitigate them with ConfigureAwait(false) or Task.Run.

So, it is not always a bad idea to continue on the same synchronization context, especially inside async void methods where unhandled exceptions are posted to the current synchronization context, see TAP global exception handler.


Updated to answer the questions in the comments:

What is the difference between await Task.Run(() => InitAsync()); and Task.Run(async () => await InitAsync()); and await Task.Run(async () => await InitAsync());

In this case (a simple async lambda to Task.Run) the difference would be just an extra overhead of async/await compiler-generated state machine, which you don't need. The task, returned by InitAsync, will get unwrapped by Task.Run automatically, either way. For more general cases, see "Any difference between "await Task.Run(); return;" and "return Task.Run()"?".

I'd use an async lambda here only if I needed to do something else after the completion of InitAsync, while still not having to worry about synchronization context, e.g.:

await Task.Run(async() => {
    await InitAsync();
    // we're on a pool thread without SynchronizationContext
    log("initialized");
});

Double check: use discard like this _ = WorkAsync(); to suppress warning, but it doesn't catch exception. To handle exception, I need to define an extension method like Forget. on Fire and Forget approach

Yes, that'd be my choice for fire-and-forget. However, I don't think your InitAsync is a true fire-and-forget in your case. Perhaps, it'd be better to keep track of it in the class instance: _task = InitAsync() and observe _task later.

Or, better yet, you could use an async void helper method inside OnCreate to observe the result/exceptions of InvokeAsync:

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

    async void function invokeInitAsync()
    {
        try 
        {
            await InitAsync();
            Finish();
        }
        catch(Exception e) {
            // handle the failure to initialize
            await promptAndExitUponErrorAsync(e);
        }
    }

    invokeInitAsync();
}

It might be possible to make OnCreate itself async void, but then exceptions (if any) from base.OnCreate() wouldn't be getting synchronously propagated to the caller of your override, which may have other side effects. Thus, I'd use a helper async void method, which can be also local as above.

Finally, consider embracing asynchrony in your ViewModel layer, and then you woudn't have to worry about it in places like OnCreate. For more details, see: "How to Unit test ViewModel with async initialization in WPF".

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • What is the difference between `await Task.Run(() => InitAsync());` and `Task.Run(async () => await InitAsync());` and `await Task.Run(async () => await InitAsync());` – Pingpong Nov 22 '19 at 23:37
  • @Pingpong, I've edited my answer to address your comments. – noseratio Nov 23 '19 at 00:25
  • I forgot to ask: Do you mean ConfigureAwait(false) is NOT required for lines A, B, C and D? – Pingpong Nov 23 '19 at 00:39
  • @Pingpong, I agree with Stephen Cleary that it's never required, on your A/B/C/D lines either. It's often uses as an optimization to avoid redundant context switches (less relevant these days) and often mis-used as a remedy to avoid deadlocks. – noseratio Nov 23 '19 at 00:50
  • Is a method with `async void` a bad practice or ok to use? Because I have an event handler that cannot have `async Task`. – Pingpong Nov 23 '19 at 01:24
  • 1
    @Pingpong there're use cases where `async void` can be appropriate, like fire-and-forget invocations, event handlers or notification-style `void` virtual overrides like your `OnCreate`. Otherwise, `async void` wouldn't be allowed by the C# compiler in the first place. – noseratio Nov 23 '19 at 04:21
2

Is ConfigureAwait(false) required

ConfigureAwait(false) is never required, unless you're using it as part of a direct blocking sync-over-async hack, which is never recommended.

If you need to stay on the same context (e.g., you're accessing UI elements), then the question is moot: you cannot use ConfigureAwait(false) because your code must resume on the same context. This is the scenario for your "opposite answer" link.

For libraries, the traditional approach is to recommend ConfigureAwait(false) everywhere, because library authors don't know how their libraries will be consumed. This was especially true because there were a few situations (primarily in ASP.NET) where sync-over-async hacks were required.

However, now that ASP.NET Core is async all the way (no longer requiring blocking hacks) and also doesn't have a context at all, the chances that a library will be used with sync-over-async is significantly reduced. So some libraries have started dropping ConfigureAwait(false) - most notably Entity Framework Core. Time will tell whether ConfigureAwait(false) will continue, or whether it will become a historical oddity.

For myself, I do use ConfigureAwait(false) in my libraries, which are commonly used on older platforms. But if you have a library that is only consumed by modern UI and Core apps, then it's not necessary.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Can you point out which section is about sync-over-async? Even better Is it possible to provide a code example? "modern UI and Core apps" is there a away to test it myelf. – Pingpong Nov 22 '19 at 23:11
  • @Pingpong: That article is about sync-over-async techniques, but you could start reading at "Vertical Partitions". – Stephen Cleary Nov 22 '19 at 23:13
  • Do you mean `ConfigureAwait(false)` is NOT required for lines A, B, C and D? Can you point out which section is about sync-over-async? Even better Is it possible to provide a code example? "modern UI and Core apps" Is it possible to provide code to test if `ConfigureAwait(false)` is required or not? – Pingpong Nov 22 '19 at 23:14
  • 1
    @Pingpong: `ConfigureAwait(false)` is not required for any of those lines. Almost the entire article is about sync-over-async strategies. `ConfigureAwait(false)` is only *required* if the calling code blocks and there's a one-thread-at-a-time context. There isn't a way to check for this in code, or else everyone would just do that and "magically do the right thing". – Stephen Cleary Nov 24 '19 at 02:14
  • I have a [related question](https://stackoverflow.com/q/59096962/2674222) and I hope you can share or opinion it. Thanks Stephen! – avo Nov 29 '19 at 03:05
  • Does ConfigureAwait(false) run on separate thread than main thread on UI applications? and we need to return back to UI thread if want to display result on the UI? – Emil Aug 12 '20 at 20:51
  • @batmaci: `ConfigureAwait(false)` controls where the code runs *after* the `await`. If you're on the UI thread before the `await` and you want to resume on the UI thread after the `await`, then you should *not* use `ConfigureAwait(false)`. – Stephen Cleary Aug 12 '20 at 23:05
  • @StephenCleary what if library has async implementations and client must execute those as sync (unfortunately because of DI on different platforms) , is it safe to use just ".Result" suffix. I went through your articles and you suggest to use configureawait false or Thread.run. I understand that it goes off the UI thread but can we not return back to UI thread. Is it a bad design? – Emil Aug 13 '20 at 12:35
  • 1
    @batmaci: My [brownfield async article](https://learn.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development) discusses several different approaches and the tradeoffs of each. – Stephen Cleary Aug 13 '20 at 12:46