简体   繁体   中英

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

Let me just post 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 app -like a console app-. I'm having a difficult time understanding how the code //Code after await would be able to run.

I understand that when I hit the await keyword in MyAsyncMethod() control goes back to MyMethod() , but then I'm locking the thread with task.Wait() . If the thread is locked, how can //Code after await ever run if the thread that is supposed to take it is locked?

Does a new thread get created to run //Code after await ? Or does the main thread magically steps out of task.Wait() to run //Code after await ?

I'm not sure how this is supposed to work?

Code as posted will "Deadlock" in Winform App if called from main thread because you're blocking the main thread with the Wait() .

But in console app this works. but how?

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

In winform app SynchronizationContext.Current will be set to WindowsFormsSynchronizationContext which will post to the call to "Message loop", but who is going to process that? out main thread is waiting in Wait() .

In console app SynchronizationContext.Current will not be set by default so it will be null when no "SynchronizationContext" available for await to capture so it will schedule the continuation to ThreadPool (TaskScheduler.Default which is ThreadpoolTaskScheduler) and so the code after await works(through threadpool thread).

Aforementioned capturing behavior can be controlled using Task.ConfigureAwait(false); which will prevent winform app from deadlocking but code after await no longer runs in UI thread.

Does a new thread get created to run //Code after await?

Maybe. Maybe not. The awaitable pattern implementation for Task runs the continuation (the bit after the await expression) using the synchronization context which was "current" when at the start of the await expression. If you're in the context of a UI thread, for example, that means that you'll end up back on the same UI thread. If you're in a thread-pool thread, you'll end up back on some thread-pool thread, but it could be a different one.

Of course with your code sample, if you're in a UI thread, your call to Wait() will block the UI thread so that the continuation can't run - you need to be careful about that. (Calling Wait() or Result on tasks that you don't know to be completed, and which may require work on the current thread, is a bad idea.)

Note that you can call Task.ConfigureAwait so that you can express the intention of not requiring to continue on the same context. This is usually appropriate for library methods which don't care which thread they run on:

await task.ConfigureAwait(false);

(It affects more than the thread - it's the whole context which is captured or not.)

I think it's a good idea to get familiar with what's going on under the hood with await. There's plenty of documentation online, and if you'll allow me a brief plug, there's also the 3rd edition of C# in Depth , and my Tekpub screencast series on the topic . Or start with MSDN and proceed from there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM