Which thread runs the code after the `await` keyword?

Let me just write a simple example:

private void MyMethod() { Task task = MyAsyncMethod(); task.Wait(); } private async Task MyAsyncMethod() { //Code before await await MyOtherAsyncMethod(); //Code after await } 

Let's say I run the above code in a single-threaded application, for example, in a console application. I find it difficult to understand how the code will work //Code after await .

I understand that when I clicked the await keyword in MyAsyncMethod() , control will return to MyMethod() , but then I block the thread using task.Wait() . If the thread is blocked, how can //Code after await start //Code after await if the thread that should receive it is blocked?

Is a new thread created to run //Code after await ? Or does the main thread magically exit task.Wait() to run //Code after await ?

I'm not sure how this should work?

+6
source share
2 answers

The code that was sent will be β€œDeadlock” in the Winform application if it is called from the main thread because you are blocking the main thread with Wait() .

But in a console application this works. but how?

The answer is hidden in SynchronizationContext.Current . await captures the "SynchronizationContext", and when the task is completed, it will continue in the same "SynchronizationContext".

The winform app SynchronizationContext.Current will be set to WindowsFormsSynchronizationContext , which will be sent to the call in the "message loop", but who will handle it? the main thread is waiting in Wait() .

The default value will not be set in the SynchronizationContext.Current console application, so it will be null if there is no "SynchronizationContext" available for waiting, to schedule a continuation to ThreadPool (TaskScheduler.Default, which is ThreadpoolTaskScheduler) and therefore the code works after waiting (via threadpool thread).

The above capture behavior can be controlled using Task.ConfigureAwait(false); , which will prevent the winform application from blocking, but the code after await will no longer be executed in the user interface thread.

+11
source

Is a new thread created to start // Code after waiting?

May be. Probably no. In the expected implementation of the template for Task , a continuation is performed (a bit after the await expression), using the synchronization context that was "current" when the wait expression was at the beginning. For example, if you are talking about a user interface thread, this means that you will return to the same user interface thread. If you are in a thread stream, you will return to the thread stream thread, but this may be different.

Of course, with your code example, if you are in a user interface thread, your Wait() call will block the user interface thread so that continuation cannot be executed - you need to be careful. (Calling Wait() or Result for tasks that you do not know to complete, and which may require working with the current thread, is a bad idea.)

Note that you can call Task.ConfigureAwait so that you can express your intention not to require a continuation in the same context. This is usually suitable for library methods that do not care about which thread they work in:

 await task.ConfigureAwait(false); 

(It affects more than a stream - this is the whole context captured or not).

I think it's nice to get to know what is happening under the hood with anticipation. There is a lot of documentation on the Internet, and if you allow me a small plugin, there is also the 3rd edition of C # in depth , and my Tekpub screencast on the topic . Or start with MSDN and get out of there.

+10
source

Source: https://habr.com/ru/post/956561/


All Articles