Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 333
AxiomaticNexus Avatar asked Oct 23 '13 13:10

AxiomaticNexus


People also ask

What happens to the thread on await?

An await expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method. The async and await keywords don't cause additional threads to be created.

What happens after await?

When execution reaches an await expression, the generated code will check whether the thing you're awaiting is already available. If it is, you can use it and keep going. Otherwise, it will add a continuation to the "awaitable" part, and return immediately.

Does await free the thread?

The await keyword, by contrast, is non-blocking, which means the current thread is free to do other things during the wait.

What does the await keyword do?

The await operator is used to wait for a Promise . It can only be used inside an async function within regular JavaScript code; however it can be used on its own with JavaScript modules.


2 Answers

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.

like image 177
Sriram Sakthivel Avatar answered Oct 15 '22 02:10

Sriram Sakthivel


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.

like image 24
Jon Skeet Avatar answered Oct 15 '22 01:10

Jon Skeet