Vincent Boilay
Vincent Boilay

Reputation: 9

Synchronization Context

I understand .ConfigureAwait(false) means the continuation does not have to be on the same thread as when entered.

So I wonder what Synchronization Context will I get back in someUIButton_click.

static async void someUIButton_click()
{
   await T1();
   // Am I sur to be on the UI thread ?
}

static async Task T1() => await doSomethingAsync().ConfigureAwait(false)

Context : C#, winforms, .Net 4.7

Upvotes: 0

Views: 1695

Answers (1)

TheGeneral
TheGeneral

Reputation: 81483

SynchronizationContext and the Execution Context

Every thread has a context associated with it, this is also known as the Current Context and these contexts can be shared across threads. The ExecutionContext contains relevant metadata of the current environment or context in which the program is in execution.

The SynchronizationContext represents an abstraction that defines the location where your application's code is executed. SynchronizationContext is a representation of the current environment that our code is running in, it provides a way to queue a unit of work to a context. The SynchronizationContext has a context associated with it and in different situations and different frameworks may represent different things. For example

  • Winform Apps – WindowsFormsSynchronizationContext
  • WPF APS – DispatcherSynchronizationContext
  • The default SynchronizationContext - Default (ThreadPool) SynchronizationContext
  • ASP.Net - AspNetSynchronizationContext

How await uses SynchronisationContext’s

Async/await is just compiler magic to make it easier to compose asynchronous stuff (it does not make your code run asynchronously), all it does when it sees an await is splits your method up into a State Machine and when the thing you are awaiting finishes executing, the state machine is resumed and your code continues running. Resuming execution is called the continuation.

A feature of the await keyword is that it captures the current SynchronizationContext before it runs the asynchronous operation, then it will post the continuation to that SynchronizationContext, meaning if you are on the UI Thread when you await, once it has finished running then your code will continue execution on the UI Thread (which is called Offloading)

ConfigureAwait()

All ConfigureAwait(false) does is configures the task so that continuation after the await does not have to be run in the caller context. This can have several benefits, one of them is a small performance gain, and more controversially stopping deadlocks in certain situations.

The Answer

Every time you call await it creates a State Machine to do the wonders of the Async Await Pattern and sets up a continuation.

Just because you have ConfigureAwait set to false nested in another await (State Machine) doesn't change the original call which is set to continue on the calling Synchronization Context (if applicable).

Side note : someUIButton_click is running unobserved and you should have appropriate error checking

Update

So that means that I can do whatever I want in T1 as long it does not interacts with some UI components

You are "seemingly" free to access UI components from both someUIButton_click and T1 however, with the exception that after the await in T1 you have configured the task so that the continuation does not need to return to the calling context. Accessing the a UI component here will likely cause you problems.

So what about after the await in someUIButton_click?

Well in that case, task has (by default) captured the current context (presumably your UI thread), and the continuation will continue on that context. Each nested await is a different case, and the calling await (state machine) gets to choose which context it continues on

Upvotes: 4

Related Questions