#code #dotnet #engineering #tools
idea
Synchronization context is an object mostly configuring the behavior of how and when to execute an asynchronous task. It contains a Post method, which is most use-cases, as the default behavior, simply adds the async delegate to a ThreadPool queue for latter execution. In some cases (e.g. WPF/WinForms) the Synchronization context is overridden to delay the execution and/or select the thread on which to execute. Synchronization contexts might also be used to limit the degree of concurrency.
SynchronizationContext sc = SynchronizationContext.Current;
s_httpClient.GetStringAsync("http://example.com/currenttime").ContinueWith(downloadTask =>
{
sc.Post(delegate
{
downloadBtn.Content = downloadTask.Result;
}, null);
});
Task schedulers are used when using Task.StartNew. They schedule tasks on certain threads. By default TaskScheduler is using ThreadPool. It might also have several schedules, such as one for tasks requiring serial execution and one for tasks that can be ran concurrently.
private static readonly HttpClient s_httpClient = new HttpClient();
private void downloadBtn_Click(object sender, RoutedEventArgs e)
{
s_httpClient.GetStringAsync("http://example.com/currenttime").ContinueWith(downloadTask =>
{
downloadBtn.Content = downloadTask.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
When using awaiters, when the callaback happens, the logic to schedule it is to check if there's a synchronization context, and if not to use the current non-default TaskScheduler. If not, then callback is just executed as part of awaited task. Logic looks like
object scheduler = SynchronizationContext.Current;
if (scheduler is null && TaskScheduler.Current != TaskScheduler.Default)
{
scheduler = TaskScheduler.Current;
}
Using ConfigureAwait(false) deactivates the awaiter logic, doesn't use scheduler for callback, and simply always execute the callback as part of the awaited task. This allows better performance (no queuing logic, not scheduling to be waited), and avoids deadlock that could result from limit on concurrency.
As a 'best practice' it's always better to use ConfigureAwait(false) unless there is a good reason (e.g. UI, or delegates in the callback that might need UI sync) no to.