2

I have two tasks that can be run in parallel to increase performance:

var task1 = _service.DoStuffAsync();
var task2 = _service.DoOtherStuffAsync();

await Task.WhenAll(task1, task2);

Now, I'm sure that these tasks are done. But per my understanding (and some real life headaches), if I call .Result on these tasks, I could cause a deadlock, even though they are complete?

From what I was reading,awaiting a completed task simply returns the result, so it seems like that is the way to go here. That makes my code look funky:

var task1 = _service.DoStuffAsync();
var task2 = _service.DoOtherStuffAsync();

await Task.WhenAll(task1, task2);

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

Is this the correct way to solve this problem and get the results of both my tasks? If not, what's the problem? Is there a better way to unwrap the tasks without calling .Result on them?

Jonesopolis
  • 25,034
  • 12
  • 68
  • 112

1 Answers1

7

if I call .Result on these tasks, I could cause a deadlock, even though they are complete?

No, you can't. If the task is complete the Result will not block, it will immediately return, but more importantly you can't prevent the task that you're waiting on from finishing when it has already finished.

Is this the correct way to solve this problem and get the results of both my tasks?

It will certainly work. You are of course right that it looks silly, as you're redundantly awaiting the tasks twice (the second time won't actually take any time, as they're done, you're just typing out the code to wait for them twice). You can omit that redundant code, and simply await them the one time:

var task1 = _service.DoStuffAsync();
var task2 = _service.DoOtherStuffAsync();

var result1 = await task1;
var result2 = await task2;
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Ohhh that makes sense. Because both tasks are started, this would produce the same result in terms of efficiency. – Jonesopolis Jan 04 '18 at 16:33
  • Side note: before switching from `WhenAll` to separate awaits check discussion on ["multiple await vs. WhenAll"](https://stackoverflow.com/questions/18310996/why-should-i-prefer-single-await-task-whenall-over-multiple-awaits). – Alexei Levenkov Jan 04 '18 at 16:50
  • @AlexeiLevenkov Why link to a question that has an incorrect answer as the top and second highest answers if you want to bring it up? – Servy Jan 04 '18 at 16:52
  • Because I disagree with your statement that exception handling when all tasks failed is identical in both cases (multiple await vs. WhenAll). I'll be happy to see explanation of why both are identical, but unfortunately your [answer](https://stackoverflow.com/a/17197745/477420) is not currently covering that part of the topic. That's why I tried to word my comment as suggestion to read through discussion rather than blindly take one side of the discussion. – Alexei Levenkov Jan 04 '18 at 17:11
  • @AlexeiLevenkov In both situations if both tasks fail then the `Task` returned by the calling method will be the exception of the first method. – Servy Jan 04 '18 at 17:54
  • Task1: `Task.Delay(1);throw new Exception();`, Task2: `Task.Delay(9000); ReportAllDone();`, Task3:`Task.Delay(9000); throw new Exception();`. With `WhenAll` you are sure execution is complete for all tasks before you hit next line of code (or return from function), with individual `await` Task2/Taks3 will continue well past completion of the function. Indeed try/catch around each `await` can solve that but code intention will be even harder to see. – Alexei Levenkov Jan 04 '18 at 18:04
  • @AlexeiLevenkov And yet instead of saying that you link to an answer saying something radically different, and also incorrect. – Servy Jan 04 '18 at 18:12
  • I don't see how "WhenAll will wait for all tasks to complete. A chain of await would abort waiting at the first exception but execution of non-awaited tasks continues." is "radically different" from my last comment... (feel free to move this to chat OR simply mark flag all comments on this post for removal) – Alexei Levenkov Jan 04 '18 at 18:57
  • @AlexeiLevenkov So you don't consider that the answer is making demonstrably false statements about what exceptions are actually thrown to be relevant, or do you think that telling people that the exceptions provided to the caller would differ when they wouldn't isn't an issue? – Servy Jan 04 '18 at 19:00
  • I sort of ignored "it propagates all errors at once" part as I don't see what usr mean saying that (can't throw many exception at a time). Overall I believe linked post has decent amount of information for one to make an informed decision. If one is interested - the blog link in comments gives more info to think about the issue http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/10217876.aspx. (I should have made my initial comment clear as "please read *carefully* through all answers and comments in ... and make your own choice" but I closed this post as duplicate I'm less concerned about it.) – Alexei Levenkov Jan 04 '18 at 19:33
  • @AlexeiLevenkov That statement is attempting to claim that the exception will contain the information from all of the awaited tasks that fault, rather than just the first, but it is in correct in that assertion because the by awaiting the task you are specifically pulling out just the first exception and re-throwing it. – Servy Jan 04 '18 at 20:03