Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Multiple await take same time like Task.WhenAll()

I understand that when you have a list of tasks, it's recommended to use await Task.WhenAll() over multiple await, due to the way that Task.WhenAll() handles exceptions. However, from my understanding of the way that "async,await" works, I wonder why the below code blocks have the same execution time:

static void Main(string[] args)
{
    MainAsync(args).GetAwaiter().GetResult();
    Console.ReadLine();
}

static async Task MainAsync(string[] args)
{
    Console.WriteLine("Starts :" + DateTime.Now.ToLongTimeString());
    var firstTask = SleepForTime(10000);
    var secondTask = SleepForTime(7000);
    var thirdTask = SleepForTime(5000);

    await firstTask;
    await secondTask;
    await thirdTask;

    Console.WriteLine("Done :" + DateTime.Now.ToLongTimeString());

    Console.ReadLine();
}

public static async Task SleepForTime(int seconds)
{
    await Task.Delay(seconds);
}

This block will take 10 seconds to be executed, which is the same for this one:

static void Main(string[] args)
{
    MainAsync(args).GetAwaiter().GetResult();
    Console.ReadLine();
}

static async Task MainAsync(string[] args)
{
    Console.WriteLine("Starts :" + DateTime.Now.ToLongTimeString());
    var firstTask = SleepForTime(10000);
    var secondTask = SleepForTime(7000);
    var thirdTask = SleepForTime(5000);

    await Task.WhenAll(firstTask, secondTask, thirdTask);

    Console.WriteLine("Done :" + DateTime.Now.ToLongTimeString());

    Console.ReadLine();
}

public static async Task SleepForTime(int seconds)
{
    await Task.Delay(seconds);
}

From my understanding, the first block should take 22 seconds, because the list of await will get executed in order one by one, as this is how async,await explained by Microsoft in MSDN. What I'm missing here? Is it something optimised the the compiler? Could someone explain what's going under the hood?

like image 851
Malik Avatar asked Jan 27 '26 05:01

Malik


1 Answers

Task.Delay internally uses a timer to notify the program when it should continue. The timer starts as soon as you call Task.Delay. In both cases you start the tasks one after another as you store the tasks in variables, only to await them later. While awaiting any of them, the timers are still going in the background and because they started more or less at the same time, they finish when the one with the longest delay finishes

var firstTask = SleepForTime(10000);
var secondTask = SleepForTime(7000);
var thirdTask = SleepForTime(5000);  

// All of the tasks are already started
Console.WriteLine("Start");
await firstTask;                       //this finishes after ~10s
Console.WriteLine("First finished");
await secondTask;                      //this finishes immediately
Console.WriteLine("Second finished");
await thirdTask;                       //this also finishes immediately
Console.WriteLine("Third finished");

All of the First/Second/Third finished messages show up almost at the same time, after 10s. You can see a change after some modification:

var firstTask = SleepForTime(5000);
var secondTask = SleepForTime(7000);
var thirdTask = SleepForTime(10000);  

// All of the tasks are already started
Console.WriteLine("Start");
await firstTask;                      //this finishes after ~5s
Console.WriteLine("First finished");
await secondTask;                     //this finishes after 2 more seconds
Console.WriteLine("Second finished");
await thirdTask;                      //this finishes after 3 more seconds
Console.WriteLine("Third finished");

Now First finished shows up after 5s, Second finished after another 2s and Third finished after another 3s.

To get the desired result you'd have to call the functions sequentially and await each one right there, like this:

Console.WriteLine("Start");
await SleepForTime(10000);            //this finishes after 10s
Console.WriteLine("First finished");
await SleepForTime(7000);             //this finishes after 7s
Console.WriteLine("Second finished");
await SleepForTime(5000);             //this finishes after 5s
Console.WriteLine("Third finished");

The SleepForTime function is a perfect candidate for some style improvements - use the suffix Async to indicate that it should be used in asynchronous code and you can return the task returned from Task.Delay itself making the code a little simpler (for you and the compiler)

public static Task SleepForTimeAsync(int seconds)
{
    return Task.Delay(seconds);
}
like image 164
Jakub Dąbek Avatar answered Jan 29 '26 18:01

Jakub Dąbek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!