# Synchronization Context in Async-Await
# Pseudocode for async/await keywords
Consider a simple asynchronous method:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
Simplifying, we can say that this code actually means the following:
Task Foo()
{
Bar();
Task t = Baz();
var context = SynchronizationContext.Current;
t.ContinueWith(task) =>
{
if (context == null)
Qux();
else
context.Post((obj) => Qux(), null);
}, TaskScheduler.Current);
return t;
}
It means that async
/await
keywords use current synchronization context if it exists. I.e. you can write library code that would work correctly in UI, Web, and Console applications.
Source article (opens new window).
# Disabling synchronization context
To disable synchronization context you should call the ConfigureAwait
(opens new window) method:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
ConfigureAwait provides a means to avoid the default SynchronizationContext capturing behavior; passing false for the flowContext parameter prevents the SynchronizationContext from being used to resume execution after the await.
Quote from It's All About the SynchronizationContext (opens new window).
# Why SynchronizationContext is so important?
Consider this example:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
This method will freeze UI application until the RunTooLong
will be completed. The application will be unresponsive.
You can try run inner code asynchronously:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
But this code won't execute because inner body may be run on non-UI thread and it shouldn't change UI properties directly (opens new window):
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
if (label1.InvokeRequired)
lable1.BeginInvoke((Action) delegate() { label1.Text = label1Text; });
else
label1.Text = label1Text;
});
}
Now don't forget always to use this pattern. Or, try SynchronizationContext.Post
(opens new window) that will make it for you:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}