23

Let's say I have two async methods

public async static Task RunAsync1()
{
    await Task.Delay(2000);
    await Task.Delay(2000);
}

and

public async static Task RunAsync2()
{
    var t1 = Task.Delay(2000);
    var t2 = Task.Delay(2000);

    await t1;
    await t2;
}

Then I use it like

public static void M()
{
    RunAsync1().GetAwaiter().GetResult();
    RunAsync2().GetAwaiter().GetResult();
}

In a result the RunAsync1 will run 4sec but RunAsync2 only 2sec
Can anybody explain why? Methods are almost the same. What is the difference?

Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116

4 Answers4

58

In the second method 2 tasks are started at the same time. They will both finish in 2 seconds (as they are running in parallel). In the first method you first run one method (2 seconds), wait for it to complete, then start the second one (2 more seconds). The key point here is Task.Delay(..) starts right when you call it, not when you await it.

To clarify more, first method:

var t1 = Task.Delay(2000); // this task is running now
await t1; // returns 2 seconds later
var t2 = Task.Delay(2000); // this task is running now
await t2; // returns 2 more seconds later

Second method:

var t1 = Task.Delay(2000); 
var t2 = Task.Delay(2000); // both are running now

await t1; // returns in about 2 seconds
await t2; // returns almost immediately, because t2 is already running for 2 seconds
Secespitus
  • 710
  • 2
  • 14
  • 22
Evk
  • 98,527
  • 8
  • 141
  • 191
  • 10
    It is not a matter of variables, but when and where you use `await`. If you did `var t1 = Task.Delay(2000);` then `await t1;`, you'd get identical results. – MoonKnight Jun 23 '17 at 09:51
14

Just examine your code:

public async static Task RunAsync1()
{
    await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
    await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
}

So the second await Task.Delay(2000); is called after the first call is finished (after 2 seconds).

While the second method,

public async static Task RunAsync2()
{
    var t1 = Task.Delay(2000); // Start a task
    var t2 = Task.Delay(2000); // Start a task

    await t1; // Wait for task to finish
    await t2; // Wait for task to finish
}

So tasks t1, and t2 run at the same time.

If you change it to

public async static Task RunAsync3()
{
    var t1 = Task.Delay(2000); // Start a task
    await t1; // Wait for task to finish

    var t2 = Task.Delay(2000); // Start a task
    await t2; // Wait for task to finish
}

you would get the same results as in RunAsync1.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pablo notPicasso
  • 3,031
  • 3
  • 17
  • 22
6

In the first case you are saying

public async static Task RunAsync1()
{
    var t1 = Task.Delay(2000);
    await t1;
    var t2 = await Task.Delay(2000);
    await t2;
}

Which equates to

  1. 0:00 Create a callback in 2 seconds 0:00
  2. 0:00 Wait until the callback has returned 0:02
  3. 0:02 Create a callback in 2 seconds 0:02
  4. 0:02 Wait until the callback has returned 0:04
  5. 0:04 return;

The second case is

public async static Task RunAsync2()
{
    var t1 = Task.Delay(2000);
    var t2 = Task.Delay(2000);

    await t1;
    await t2;
}
  1. 0:00 Create callbacks in 2 seconds 0:00
  2. 0:00 Create callbacks in 2 seconds 0:00
  3. 0:00 Wait for first callback 0:02
  4. 0:02 Wait for the second callback 0:02
  5. 0:02 return

In other words, in the first one you are doing sequential asynchronous programming, and the second is parallel asynchronous programming.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Aron
  • 15,464
  • 3
  • 31
  • 64
  • Shouldn't the second **4.** be `0:00 Wait for the second callback 0:02`? – camden_kid Jun 23 '17 at 12:32
  • 1
    @camden_kid: No, since after step 3 you already are at 0:02 (you already waited 2 seconds there). – hoffmale Jun 23 '17 at 14:27
  • @hoffmale It's slightly confusing the way it is written. At 0.02 it's not really waiting as it has just finished waiting. – camden_kid Jun 23 '17 at 14:58
  • 1
    @camden_kid: in step 3, you are waiting until task 1 is finished. That means waiting until 0:02, as that is when task 1 is finishing. In step 4, you are waiting until task 2 is finished. As it just happens, task 2 is finishing in that exact moment, so there is not much of a delay. "Waiting for a task to finish" can mean "return instantly if said task is already finished". – hoffmale Jun 23 '17 at 15:17
  • @hoffmale Fair enough. Thanks. – camden_kid Jun 23 '17 at 15:19
5

Whenever you start a Task. It already started when you created it, not when you called await.

If you create a task and put it in a variable, it might already finish when you await that. This is what happen to your second case. await just ensures that it must finish before continuing.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thaina Yu
  • 1,372
  • 2
  • 16
  • 27