Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where do 'awaited' tasks execute?

Consider the following:

private async void btnSlowPoke_Click(object sender, EventArgs e)
{
    await DoItAsync();
}

private async Task<int> SomeLongJobAsync()
{
    for (int x = 0; x < 999999; x++)
    {
        //ponder my existence for one second
        await Task.Delay(1000);
    }
    return 42;
}

public async Task<int> DoItAsync()
{
    Console.Write("She'll be coming round the mountain");
    Task<int> t = SomeLongJobAsync();  //<--On what thread does this execute?
    Console.WriteLine(" when she comes.");
    return await t;
}
  1. The first Write in DoItAsync() executes.
  2. SomeLongJobAsync() starts.
  3. The WriteLine in DoItAsync() executes.
  4. DoItAsync() pauses while SomeLongJobAsync() works away until it's done.
  5. SomeLongJobAsync() completes, so DoItAsync() returns.

Meanwhile, the UI is responsive.

On what thread does SomeLongJobAsync() execute?

like image 441
Eric Avatar asked Jun 26 '14 22:06

Eric


People also ask

What does await Task Run do?

As you probably recall, await captures information about the current thread when used with Task. Run . It does that so execution can continue on the original thread when it is done processing on the other thread.

What happens when you await a Task C#?

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any.

What happens during await?

await releases the current thread, but NOT to the thread pool. The UI thread doesn't come from the thread pool. If you run asynchronous method, e.g. ExecuteScalarAsync without async, await keywords, then this method will run asynchronously no matter what. The calling thread won't be affected .

Does await start the Task?

No, async await is just made to allow code to run whilst something else is blocking, and it doesn't do Task. Run, or start a new thread.


2 Answers

Short Answer

An async method fired by the GUI thread will execute on the same thread, whenever there are CPU operation to execute. Other async methods start running on the calling thread and continue on a ThreadPool thread.

Long Answer

SomeLongJobAsync starts executing on the calling thread (the one that printed "She'll be coming round the mountain") up until it reaches an await. Then a task is returned that represents the asynchronous operation + the continuation after it. When the entire operation is done the task will complete (unless it completes prematurely due to an exception or cancellation).

When Task.Delay(1000) itself is "executing" there is no thread, because none is needed. And when finally Task.Delay(1000) ends, a thread is needed to resume on. Which thread it is depends on the SynchronizationContext (by default there is none so the thread is a ThreadPool thread, but in a GUI application it's the singe GUI thread, more here). That thread executes the rest of the code until it reaches another asynchronous point (i.e. another await) and so forth and so forth.

like image 145
i3arnon Avatar answered Nov 04 '22 00:11

i3arnon


The important thing to realise is that async is not about creating threads, it's about replacing what used to be a blocking call by one that returns a continuation. A thread blocks when it is placed on a queue and it can then do nothing until the thing it blocked on becomes available (or a timeout or exception, etc). Blocking the UI thread is a terrible thing to do.

By contrast a continuation contains enough information captured at a point in the program for the thread to carry on ("continue") from that exact point at a later time. Obviously there is special stuff needed in all the Async calls for that to work, but that's what it does. It freezes the thread state into a continuation, parks that somewhere, returns immediately (so no blocking) and then (somehow) starts again from that state at a later time when the required information is available.

For that reason, you can assume that the work for both the async method and the "long job" will be done on the same thread, just not at the same time, and that the operating system will choose a good time to decide when to make those choices.

In practice there is a difference between threads that have a message pump (UI threads) and others, and there is the possibility of work being moved to a different thread, and there are various features in the Task, the SynchronizationContext and thread pools to support more advanced scenarios.

But I think the key to answering your question and for you to understand is this subtle use of something new called a continuation, and how it can capture the state of a program at one time for use later. Continuations have been used in functional languages for a long time, and are in some ways related to the concepts of futures and promises in other languages. Once you think in these terms you can forget about threads entirely.

like image 29
david.pfx Avatar answered Nov 04 '22 02:11

david.pfx