2

I'm a bit confused when it comes to the async/await stuff in .NET...

Consider the following method:

public async Task DoSomething() {
   IEnumerable<Task> ts = GetSomeTasks(); // Some tasks that would do some random IO stuff, or whatever
   await Task.WhenAll(ts);
   Console.WriteLine("All tasks completed!");
}

Is the call to Console.WriteLine guaranteed to be executed after the tasks in ts have been awaited? I think I've seen cases where await doesn't seem to "block" like that until the task result is accessed. What rules apply?

Erik Kinding
  • 1,742
  • 2
  • 15
  • 32
  • 3
    You may have seen some cases where people inadvertently created `Task`s (or `Task>`s) - where the outer `Task` completes trivially but the inner task, doing the actual work, is not yet complete – Damien_The_Unbeliever Apr 01 '20 at 08:00
  • Does this answer your question? [WaitAll vs WhenAll](https://stackoverflow.com/questions/6123406/waitall-vs-whenall) and [Why does Task.WaitAll() not block or cause a deadlock here?](https://stackoverflow.com/questions/22699048/why-does-task-waitall-not-block-or-cause-a-deadlock-here) as well – Pavel Anikhouski Apr 01 '20 at 08:05
  • @PavelAnikhouski I found those questions before posting my own; it's similar but didn't really satisfy me. Now that I've been answered here the answers to those questions make more sense. – Erik Kinding Apr 01 '20 at 08:12
  • @Damien_The_Unbeliever Interesting - thank you! – Erik Kinding Apr 01 '20 at 08:12
  • The word "block" is associated with threads. In this case you have an asynchronous workflow, not a thread, so to avoid misunderstandings you could use the word "suspend". This is the word used in the documentation of the [`await`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/await) operator: *The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes.* – Theodor Zoulias Apr 01 '20 at 08:42

3 Answers3

7

Is the call to Console.WriteLine guaranteed to be executed after the tasks in ts have been awaited?

Yes.

I think I've seen cases where await doesn't seem to "block" like that until the task result is accessed.

No, that's not the case. You'll only get to the statement after the await operator (or potentially the next expression within a statement, e.g. for (await a) + (await b) once the thing that's being awaited has completed.

Note that Task.WhenAll is not a blocking method in itself (unlike Task.WaitAll) - but it returns a task that does not complete until all the tasks passed as arguments have completed.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    I think you omitted a "not" (or for "Note" you meant "Not") in your last sentence, which changes the semantic somewhat. – sellotape Apr 01 '20 at 08:03
  • Right, thank you. Would the same apply if I'd have something like "await myC.Close(); await myC.Dispose();" in a method that handles for example a connection in a class instance named myC? Imagine that it's important to call them in sequence, i.e. first Close, then Dispose. Is that safe? – Erik Kinding Apr 01 '20 at 08:05
  • 2
    @ErikKinding: Yes - although neither Close() nor Dispose() sound like asynchronous methods. – Jon Skeet Apr 01 '20 at 08:39
4

From Task.WhenAll Method documentation:

Creates a task that will complete when all of the supplied tasks have completed.

The first example on this page has the following text:

The following example creates a set of tasks that ping the URLs in an array. The tasks are stored in a List collection that is passed to the WhenAll(IEnumerable) method. After the call to the Wait method ensures that all threads have completed....

This means that the answer to your question is Yes - The call to Console.WriteLine will happen only after all the tasks have completed.

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
2

Yes - you are guaranteed that Console.WriteLine is executed after all tasks in ts have completed because you await it. This is documented here: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall?view=netframework-4.8