While waiting for a Task , the SynchronizationContext current thread is captured (especially in the case of Task TaskAwaiter ). Then the continuum then returns to that SynchronizationContext to execute the rest of the method (the part after the await keyword).
Let's look at your sample code:
private async Task Job() {
When you await Task.Delay(2000) , the compiler implicitly captures the SynchronizationContext , which is currently your WindowsFormsSynchronizationContext . When the wait is returned, the continuation is executed in the same context, since you did not explicitly specify this, but this is your UI thread.
If you changed your code to await Task.Delay(200).ConfigureAwait(false) , the continuation will not be redirected back to your current SynchronizationContext and will start the ThreadPool thread, as a result of which the update of the user interface element will throw an exception.
In the example of your timer, the Elapsed event is Elapsed by the ThreadPool thread, so you get an exception that you are trying to update, an element that is being controlled by another thread.
Now let's look at your questions one by one:
Why am I not getting an exception?
As said, await Task.Delay(2000) performed the Continuation in the user interface thread, which allowed updating the controls.
Is it possible to enhance properties in async Task?
I'm not sure what you mean by "Raise properties", but if you mean raising the INotifyPropertyChanged event, then yes, itβs normal to execute them not in the context of the user interface thread.
Is it okay to modify the ObservableCollecition in async Task, or do I need to return the Task and after receiving the result change the Observable - Clear this and fill it?
If you have an async method and you want to update the associated UI element, make sure you marshal the continuation in the user interface thread. If the method is called from the user interface thread, and you await its result, then the continuation will be implicitly launched in your user interface thread. If you want to unload the work into the background thread via Task.Run and make sure that your continuation is running in the user interface, you can write your SynchronizationContext using TaskScheduler.FromCurrentSynchronizationContext() and explicitly pass the continuation to it
when I'm in Task in the UI thread, and when not?
A Task is a promise to work that will be made in the future. when you are await on TaskAwaitable from the context of the user interface thread, you are still working in the user interface thread. You are not in the user interface thread if:
- Your asynchronous method is currently executing from a thread other than a user interface thread (either a
ThreadPool thread or a new Thread ) - You transfer work to the ThreadPool background thread using
Task.Run .
in the code above in firstButton_Click, is it ok to manage user interface elements after waiting for a job? Have I always returned to the user interface thread?
You will return to the UI thread until you explicitly indicate that your code does not return to its current context using ConfigureAwait(false)