What does awaiting an asynchronous method do in background?

c# async/await explained
async/await c#
c# call async method without await
wpf async
c# async without await
difference between async and await in c#
c# call synchronous method asynchronously
c# wait for async method to complete

I've red various articles about async await and i'm trying to understand the await async in depth. My problem is that i found out that awaiting an asyncronous method doesn't creatE a new thread, it rather just make the UI responsive. If it's like that there's no time gain when using await async since no extra thread is used.

What i knew so far is that only Task.Run() create a new thread. Is this also true for Task.WhenAll() or Task.WhenAny() ?

Let's say we have this code :

    async Task<int> AccessTheWebAsync()
            {
                using (HttpClient client = new HttpClient())
                {
                    Task<string> getStringTask = client.GetStringAsync("https://docs.microsoft.com");

                    DoIndependentWork();

                    string urlContents = await getStringTask;

                    return urlContents.Length;
                }
            }

What i expect :

  1. When creating the getStringTask Task, another thread will copy the current context and start executing the GetStringAsync method.

  2. When awaiting getStringTask, we will see if the other thread has completed his task, if not the control will be back the caller of AccessTheWebAsync() method until the other thread complets it's task to resume the control.

So i really don't get how no extra thread is created when awaiting a Task. Can someone explain what's exactly happening when awaiting a Task ?

Asynchronous programming, They are supported by the async and await keywords. The best way to handle this is to start a background thread, which does the work using Task.Run task is complete. await can only be used inside an async method. an async method basically means, it can run in background, leaving the calling thread free to do other stuffs. The single most important thing to understand here is, the method suspends the current work-flow at await and waits for the long-running task to complete asynchronously, without blocking the calling thread.

If it's like that there's no time gain when using await async since no extra thread is used.

This is correct. By themselves, async and await do not directly use threads. Their purpose is to free up the calling thread.

What i knew so far is that only Task.Run() create a new thread. Is this also true for Task.WhenAll() or Task.WhenAny() ?

No; neither Task.WhenAll nor Task.WhenAny directly use any threads.

When creating the getStringTask Task, another thread will copy the current context and start executing the GetStringAsync method.

No. GetStringAsync is invoked synchronously on the current thread, just like any other method. It returns an incomplete task, again, synchronously.

When awaiting getStringTask, we will see if the other thread has completed his task, if not the control will be back the caller of AccessTheWebAsync() method until the other thread complets it's task to resume the control.

Close, except that there's no other thread. await getStringTask will check to see if the task is completed; if it's not, then it will return an incomplete task from AccessTheWebAsync.

Can someone exmplain what exactly happening when awaiting a Task ?

I recommend reading my async intro for more details.

Async/Await, These days there's a wealth of information about the new async and await support in the Microsoft . Figure 2 Exceptions from an Async Void Method Can't Be Caught with Catch. XML Copy Retrieve the result of a background task, Task. The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method. Recognize CPU-bound and I/O-bound work

Your basic assumption — a Task always runs on a thread — is indeed wrong. A simple counterexample would be a timer-based task which doesn't run at all: it just subscribes on timer and sets task state to completed whenever the timer fires.

More useful and more practical example of the tasks not running anywhere — network requests: they send the request, subscribe to the incoming answer and just stop running, freeing thread for another work*.

So let's consider your actual questions.


What i knew so far is that only Task.Run() create a new thread. Is this also true for Task.WhenAll() or Task.WhenAny() ?

No, Task.WhenAll won't create any new threads. It will wait for the already existing tasks to complete regardless of where they run (and regardless of if they run in any thread at all!).

The task created by Task.WhenAll is not running in any specific thread itself! It just detects when the underlying tasks are finished, and after all of them are ready, finishes itself too. Task.WhenAll doesn't need any thread for doing this.


When creating the getStringTask Task, another thread will copy the current context and start executing the GetStringAsync method.

Calling an async method like GetStringAsync, as we seen before, won't be executed on any specific thread. The code of GetStringAsync sets up the things so that it gets control back (perhaps on a thread pool thread) when the answer comes, and yields control back to you. The preparation work can be perfectly done on current thread, it doesn't take too much time*.


*Disclaimer: it's a simplification, indeed the sequence of actions done by a network async request is much more complicated.

C# async, await Examples, We use the async and await keywords to asynchronously run a method. Part 2: This async method displays a status message, and does some long-running Tasks; class Program { static void Main() { // Run a Task in the background. The async method is on pause until the awaitable is complete (wait), but the thread is not blocked by this call (it is asynchronous). Something important about awaitables: the type of data returned is awaitable, not the method itself. This means that one can await the result of an async method because it returns a Task, not for being marked as

An article that helped me a lot to understand async-await is this interview with Eric Lippert, where he compares async-await with a cook making breakfast. Search somewhere in the middle for async-await.

If a cook has to make breakfast and he just put some bread in the toaster, he doesn't wait idly for the bread to be toasted, but starts looking around to see if he can do something else, for instance boiling water for the tea.

Something similar happens when you see async-await. If you call an async function, you know that somewhere inside is an await. In fact, your compiler will warn you if you forget to await in your async function.

Once your thread sees the await, it does not wait idly for the awaitable task to finish, but it looks around to see if it can do other things. It can go up the call stack to see if one of the callers is not awaiting yet and execute those statements until it sees an await. Go up the call stack again and execute statements until you see an await.

There is no guarantee that the thread that continues the statements after your not-awaited async call is the same as your original thread. But because this thread has the same "context" you can act as if it is the same thread. There is no need for a critical section and the like.

Console.Writeline(Thread.CurrentThread.ManagedThreadId);

// async call to the text reader to read a line; don't await
var taskReadLine = myTextReader.ReadLineAsync()

// because I did not await, the following will be executed as soon as a thread is free
Console.Writeline(Thread.CurrentThread.ManagedThreadId);
...

// we need the read line; await for it
string readLine = await taskReadLine;
Console.Writeline(Thread.CurrentThread.ManagedThreadId);
ProcessReadLine(readLine);

There is no guarantee that the thread that does the DoSomething is the same thread that was used to call the ReadLineAsync. If you execute the code in a simple test program, chances are high that you get more than one thread id.

Your code should not depend on any statement within the async function to be executed before you await the result:

async Task<int> DoIt()
{
    this.X = 4;
    await DoSomethingElseAsync(this.X);
    return 5;
}
async Task CallDoItAsync()
{
    this.X = 0;
    var taskDoIt = DoIt();

    // you didn't await, it is not guaranteed that this.X already changed to 4
    ...
    int i = await taskDoIt();
    // now you can be certain that at some moment 4 had been assigned to this.X 

Creating a Task object does not create a thread. Creating a thread is fairly expensive. Therefore your process has a thread pool, containing several threads. Threads that are idle are put in the pool and are available to do other things upon request. As soon as your process needs a thread it takes an available thread from the thread pool and schedules it for running.

I'm not sure what happens if no thread if available in the pool. I guess that your function simply has to wait for an available thread.

You can access the thread pool using the static ThreadPool class.

ThreadPool.GetMaxThreads (out int workerThreads, out int completionPortThreads);
++workerThreads;
++completionPortThreads;
bool success = ThreadPool.SetMaxThreads (workerThreads, completionPortThreads);

Be very careful changing the thread pool!

Some people say that async-await is only useful to keep the UI responsive, but the following shows that it also can improve processing speed.

Non-async:

void CopyFile(FileInfo infile, FileInfo outFile)
{
     using(var textReader = inFile.OpenText())
     {
        using (var textWriter = outFile.CreateText())
        {
            // Read a line. Wait until line read
            var line = textReader.ReadLine();
            while (line != null)
            {
                // Write the line. Wait until line written
                textWrite.WriteLine(line);

                // Read the next line. Wait until line read
                line = textReader.ReadLine();
            }
        }
    }
}

You see all the waits. Luckily a TextReader and TextWriter do buffer data, otherwise we really had to wait until the data was written before the next line was read

async Task CopyFileAsync(FileInfo infile, FileInfo outFile)
{
     using(var textReader = inFile.OpenText())
     {
        using (var textWriter = outFile.CreateText())
        {
            // Read a line. Wait until line read
            var line = await textReader.ReadLineAsync();
            while (line != null)
            {
                // Write the line. Don't wait until line written
                var writeTask = textWrite.WriteLineAsync(line);

                // While the line is being written, I'm free to read the next line. 
                line = textReader.ReadLine();

                // await until the previous line has been written:
                await writeTask;
            }
        }
    }
}

While a line is being written, we already try to read the next line. This can improve processing speed.

Using Task.Run in Conjunction with Async/Await, But what about when you need to call some predefined method that does not return a Task ? If it is some trivial operation that executes quickly,� The problem here is the method client.GetData() is not, in and of itself, an asynchronous method. By putting an asynchronous wrapper over a synchronous method, we are doing an antipattern known as async-over-sync and this ends up being a bad idea most of the time. Not because it doesn't work (it does) but because it isn't efficient.

Async/await, In computer programming, the async/await pattern is a syntactic feature of many programming Instead, it will begin the download process using a non-blocking mechanism (such as a background thread), and immediately return an A function using async/await can use as many await expressions as it wants, and each� Most async methods return Task, but not all Task-returning methods are necessarily async. That can be a bit of a mind-bender. That can be a bit of a mind-bender. Let's say you need to implement an a method that returns Task or Task<TResult> , but you don't actually have anything async to do.

How does Async-Await work, an async method basically means, it can run in background, leaving the calling thread free to do other stuffs. The single most important thing to� The RunWorkerAsync method submits a request to start the operation running asynchronously. When the request is serviced, the DoWork event is raised, which in turn starts execution of your background operation. If the background operation is already running, calling RunWorkerAsync again will raise an InvalidOperationException.

Module: Concurrent::Async — Concurrent Ruby, These proxy methods post the method call to the object's background thread and Calling a method with async will return a :pending IVar whereas await will� The preceding method has the async modifier in its signature. That signals to the compiler that this method contains an await statement; it contains asynchronous operations. This method represents the task that toasts the bread, then adds butter and jam. This method returns a Task<TResult> that represents the composition of those three operations.

Comments
  • Task.Run will use the threadpool, so it will most likely run on a different thread, but it doesn't necessarily mean that it will always create a new thread just for your task.
  • Okay, what about Task.WhenAll ? will it use different multiple threads in the threadpool ? Do you also have an answer to my last question ?
  • The whole point of tasks is to allow you to run something in the background, therefore not blocking the UI. There are other things you can do with them, such as running multiple tasks in parallel, but generally speaking don't think of tasks as anything to do with threads (even though they are).
  • Possible duplicate of If async-await doesn't create any additional threads, then how does it make applications responsive?
  • read Stephen Cleary There is no thread you'll find it is referenced in almost every good answer in SO regarding async/await
  • I appreciate your great answer, thank you very much. I just want to confirm something. Is it always the case that there's no thread created when awaiting a task. Let's say we await a CPU bound task that does heavy calculations. What i know so far is a I/O bound code it will be executed on low level CPU components (much lower than threads) and only use a thread briefly to notify the context about the finished Task status. I also know that we use Task.Run() to execute CPU bound code to look for an available thread in thread pool. Is this true ?
  • @SoufienHajji: You're welcome. I've addressed your follow-up question in the answer.
  • The joy of building a "multithreaded" UI using old line-characters on the IBM/PC DOS using TurboPascal and faking it all by saving the registers and jumping to the next "thread" by restoring registers from memory, including the instruction pointer. Brings back memories. As always, Eric Lippert kills it.
  • @LasseVågsætherKarlsen: When I was a co-op student I wrote -- as production code that shipped to customers -- an implementation of fibers on top of the character-mode NetWare server OS. There was some weird thing you had to do to safely move the stack register but I cannot for the life of me remember what it was. I look back on that and I am half astonished and half horrified that they shipped my hacked-up implementation to real customers who depended on it. Netware ran user programs in the zero ring! Any mistake could wipe out any memory.
  • @EricLippert Probably your answer is one of the best answers i've ever had, and the music joke was hilarious. Thank you very much ^_^ i couldnt ask for more :)
  • Thanks. I just finished reading your article I learn that only Task.Run will look for available threads in the thread pool to do CPU bound code. As for I/O bound code it will be executed on low level CPU components and not threads and only use a thread briefly to notify the context about the finished Task status.
  • Thanks for your answer. For my first question, let's say we have Task.WhenAll( 5 Tasks). Provided that all 5 Tasks don't need any synchronisation, these task will run in parallel, does that mean that there will be 5 threads used in the same time in our thread pool (If we find 5 threads directly available of course) ?
  • @SoufienHajji: Let me reiterate that the tasks are not running on a thread. So 1000 running tasks doesn't mean 1000 threads taken.