8

In the following code task1 and task2 are independent of each other and can run in parallel. What is the difference between following two implementations?

var task1 = GetList1Async();
var task2 = GetList2Async();

await Task.WhenAll(task1, task2);

var result1 = await task1; 
var result2 = await task2; 

and

var task1 = GetList1Async();
var task2 = GetList2Async();

var result1 = await task1; 
var result2 = await task2; 

Why should I choose one over the other?

Edit: I would like to add that return type of GetList1Async() and GetList2Async() methods are different.

Snakebyte
  • 3,735
  • 3
  • 16
  • 12
  • 1
    I4v, in the first case, `var result = await task1;` would be the exact same of `var result = task1.Result`. I do think that `.Result` is more readable though. – dcastro Sep 11 '13 at 17:18
  • @I4V Nope. In both snippets the tasks are run in parallel, not sequentially. – Servy Sep 11 '13 at 17:31
  • 2
    @dcastro, they are not the same. In the case of an exception, using .Result results in different exception being generated (an `AggregateException`) than when using `await` (the first exception), so when using `async/await` you should prefer using `await` over `.Result` to keep exception behavior consistent. – Matt Smith Sep 11 '13 at 17:55
  • But if there's an exception it'll be thrown when awaiting all, and you'll never get to the two lines below that. Assuming you do get to the two lines below that, you can .Result without worrying about exceptions or deadlocking because it's guaranteed the tasks have already completed. Refer to http://stackoverflow.com/a/24657079/1676558. – user1676558 Apr 01 '17 at 03:47
  • I was searching through this case and I still couldn't find the documentation that I need. But there are school of people that they say: that once you call a async method, the task start automatically on call, and await only wait for it's completion, and if the task is done it only return the result. so await Task.WhenAll only add more readability in this case. but after several seeing such answers, I'm not still sure, if you saw the docs, you may want to share with us here. – Hassan Faghihi Jan 16 '21 at 07:29

2 Answers2

11

Your first example will wait for both tasks to complete and then retrieve the results of both.

Your second example will wait for the tasks to complete one at a time.

You should use whichever one is clearer for your code. If both tasks have the same result type, you can retrieve the results from WhenAll as such:

var results = await Task.WhenAll(task1, task2);
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Apart from intent clarity, if the conditions are right (2 or more CPU cores are idle) is the chances of solution 1 being parallel more than solution 2 ? – Snakebyte Sep 11 '13 at 17:35
  • 4
    @Snakebyte No, there is not. They'll both perform identically, with only a very tiny bit of extra overhead in the first case as it's performing a `WhenAll` needlessly. – Servy Sep 11 '13 at 17:36
  • @Servy: What do you mean exactly? As I understand and Stephan states is that in example1 task1 and task2 are executed in paralel. In example 2 they are not executed paralel, task 1 is executed and the main thread returns when the result is completed and starts executing task 2. – Maarten Kieft Feb 06 '15 at 14:02
  • 2
    @BlackHawkDesign: No, in both cases they *execute* concurrently. The question is whether the parent method *waits* for them concurrently or one at a time. – Stephen Cleary Feb 06 '15 at 14:05
  • 1
    @Stephen Cleary: I see what you mean, you/servery are right. I interpreted the 2nd piece of code as: var task1 = await GetList1Async(); var task2 = await GetList2Async(); In that case they would be executed after each other. But you are right, in this case they are both executed in paralel. – Maarten Kieft Feb 10 '15 at 11:42
2

The first construct is more readable. You clearly state that you intend to wait for all tasks to complete, before obtaining the results. I find it reasonable enough to use that instead the second.

Also less writing, if you add a 3rd or 4th task... I.e. :

await Task.WhenAll(task1, task2, task3, task4);

compared to:

var result1 = await task1; 
var result2 = await task2; 
var result3 = await task3; 
var result4 = await task4; 
Stas
  • 286
  • 1
  • 3
  • 4
    But in the first sample he goes and awaits all of the component tasks anyway. It's only a win if you avoid doing that and use the return value of `WhenAll` instead. – Servy Sep 11 '13 at 17:30
  • What a 'win' is seems subjective in this case. Task.WhenAll is not completely the same as awaiting the tasks individually (as Servy knows). Take a look at http://stackoverflow.com/q/18310996/1676558 if you want to start learning more about this issue. – user1676558 Apr 01 '17 at 04:37