I would like to contribute with some examples even though Patrick Hofman gave the answer (especially by pointing to this section in the linked article https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?redirectedfrom=MSDN#task-status).
Hopefully the examples can help with the interpretation.
If you start by just making some simple HOT tasks you would see they start as soon as they have been initialised.
// Using just some HOT task
var first = Task.Delay(2_500);
var second = Task.Delay(2_500);
await Task.Delay(2_500);
Console.WriteLine("Start awaiting HOT tasks");
var stopwatch = Stopwatch.StartNew();
await Task.WhenAll(first, second);
Console.WriteLine(stopwatch.Elapsed);
The above code snippet will output below which validates that the tasks has been running between the initialisation and await Task.WhenAll(…)
.
Start awaiting HOT tasks
00:00:00.0000866
Now if you instead used the Task constructor the task wouldn’t be started but be in state TaskStatus.Created
. We need to start them explicitly as in below example:
// Using Task constructor
var firstNew = new Task(() => Task.Delay(2_500).GetAwaiter().GetResult());
var secondNew = new Task(() => Task.Delay(2_500).GetAwaiter().GetResult());
await Task.Delay(2_500);
Debug.Assert(firstNew.Status == TaskStatus.Created && secondNew.Status == TaskStatus.Created);
firstNew.Start();
secondNew.Start();
Console.WriteLine("Start awaiting NEW tasks");
stopwatch.Restart();
await Task.WhenAll(firstNew, secondNew);
Console.WriteLine(stopwatch.Elapsed);
above code gives output:
Start awaiting NEW tasks
00:00:02.5070535
If you would use either Task.Run
or Task.Factory
they would return started task. As a example this code:
// Run
var firstRun = Task.Run(async () => await Task.Delay(2_500));
var secondRun = Task.Run(async () => await Task.Delay(2_500));
await Task.Delay(2_500);
Console.WriteLine("Start awaiting RUN tasks");
stopwatch.Restart();
await Task.WhenAll(firstRun, secondRun);
Console.WriteLine(stopwatch.Elapsed);
// Factory
var firstFactory = Task.Factory.StartNew(async () => await Task.Delay(2_500));
var secondFactory = Task.Factory.StartNew(async () => await Task.Delay(2_500));
await Task.Delay(2_500);
Console.WriteLine("Start awaiting FACTORY tasks");
stopwatch.Restart();
await Task.WhenAll(firstFactory, secondFactory);
Console.WriteLine(stopwatch.Elapsed);
outputs:
Start awaiting RUN tasks
00:00:00.0002207
Start awaiting FACTORY tasks
00:00:00.0005118
DoSomethingAsync()
will immediatly run as far as possible - which means e.g. up to the first point where it awaits some inner task inside which has not yet finished. Only then it will return and the continuation will get attached. IfDoSomethingAsync()
has dozens of nested await functions inside, where all complete synchronously (e.g. the last one is aTask.FromResult()
) thenmyTask
will be completed before the control is given back to the caller. – Ebony