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
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 whetherConfigureAwait(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.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 withTask.Run
or use something likeTaskExt.WithNoContext
from the above link: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
orConfigureAwait
:This would give you a chance to discover and investigate potential deadlocks, before trying to mitigate them with
ConfigureAwait(false)
orTask.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:
In this case (a simple
async
lambda toTask.Run
) the difference would be just an extra overhead of async/await compiler-generated state machine, which you don't need. The task, returned byInitAsync
, will get unwrapped byTask.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 ofInitAsync
, while still not having to worry about synchronization context, e.g.: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 insideOnCreate
to observe the result/exceptions ofInvokeAsync
:It might be possible to make
OnCreate
itselfasync void
, but then exceptions (if any) frombase.OnCreate()
wouldn't be getting synchronously propagated to the caller of your override, which may have other side effects. Thus, I'd use a helperasync 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 likeOnCreate
. For more details, see: "How to Unit test ViewModel with async initialization in WPF".