2

I've was playing around with async and I've happened across some behaviour I've not noticed before, if this is a duplicate please let me know, but my google-fu has failed me, mostly because I can't think of decent terms to search:

Given a simple async method that does some parameterized work:

async Task<String> Foo(int i)
{
    await Task.Delay(i);
    return i.ToString();
}

And a calling method to invoke it in different contexts and bundle the result:

async Task<Object> Bar()
{
    var one =    Foo(3000);
    var two =    Foo(5000);
    var three =  Foo(3000);

    var x =
        new
        {
            One =   await one,
            Two =   await two,
            Three = await three,
        };      
        
    return x;
}

This completes (in Linqpad6, .NET Core 3.1) in 5 seconds. So I assume each task runs at the same time.

However, if I change it to do the await at the start, it completes in 11 seconds. So I assume each task runs sequentially.

async Task<Object> Bar()
{
    var one =   await Foo(3000);
    var two =   await Foo(5000);
    var three = await Foo(3000);

    var x =
        new
        {
            One =   one,
            Two =   two,
            Three = three,
        };
        
    return x;
}

My question is, what is it about assigning the task, then awaiting it, versus just awaiting it that allows them to complete in parallel?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
DaveShaw
  • 52,123
  • 16
  • 112
  • 141
  • 2
    It doesn't. How you await the task has nothing to do with how it runs – Panagiotis Kanavos Nov 02 '20 at 13:21
  • 2
    That is not the difference, the difference is that you started each task before you awaited any of them. In the second case you await the first before you start the next. That is the difference, and not that it's part of an assignment expression. – Lasse V. Karlsen Nov 02 '20 at 13:21
  • 1
    A `Task` is a *promise* that something will complete in the future. It's that "something" that executes asynchronously, in parallel, or in your case, not at all - `Task.Delay` creates a single-fire timer and returns a `Task` created by a TaskCompletionSource that's triggered when the timer fires – Panagiotis Kanavos Nov 02 '20 at 13:22
  • Possible duplicate: [Perform Multiple Async Method Calls Sequentially](https://stackoverflow.com/questions/26997207/perform-multiple-async-method-calls-sequentially) (probably better duplicates than this exist) – Theodor Zoulias Nov 02 '20 at 13:36
  • Sorry, but I don’t completely agree with the other comments. In my opinion it has to do with the asigning. In the first case, they run in parallel because they are awaited "while contructing an object". In this case the Properties are assigned in parallel....so the Tasks are also awaited this way. – Nikolaus Nov 02 '20 at 13:53

1 Answers1

8

See comments in line

async Task<Object> Bar()
{
    var one =    Foo(3000); // <-- It can Start
    var two =    Foo(5000); // <-- It can Start
    var three =  Foo(3000); // <-- It can Start

    var x =
        new
        {
            One =   await one,  // <-- you are waiting for it to finish
            Two =   await two,  // <-- you are waiting for it to finish
            Three = await three,// <-- you are waiting for it to finish
        };      
        
    return x;
}

async Task<Object> Bar()
{
    var one =   await Foo(3000); // <-- you are waiting for it to finish
    var two =   await Foo(5000); // <-- you are waiting for it to finish
    var three = await Foo(3000); // <-- you are waiting for it to finish

    var x =
        new
        {
            One =   one,
            Two =   two,
            Three = three,
        };
        
    return x;
}
Richard Hubley
  • 2,180
  • 22
  • 29