I think the difference between the 2 outcomes is whether the continuation from taskCompletionSource.SetResult()
is executed synchronously or asynchronously. You can (kinda) test this by providing TaskCreationOptions to force continuations to run asynchronously when creating the TaskCompletionSource: new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
See this answer by Stephen Cleary (who has written multiple great blog posts on async/await and the task api) for more info. If you really want to go into detail on TaskCompletionSource you might also be interested in this article https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
From my understanding:
If the continuation is executed asynchronously, it is by default scheduled on the thread pool (in console applications), so Print("two")
can immediately continue running on the current thread (which in this case is the main thread). If the continuation is executed synchronously, it is executed on the current thread before Print("two")
is executed. The await Task.WhenAll
seems to add the Task
as a continuation to all awaited tasks (see Source.Dot.Net, which means that this continuation again might run synchronously, effectively running before Print("two")
.