Wait for Task
Blocking & Non-Blocking Wait
Waiting a task means the execution of code is synchronous but there's a essential difference between Blocking Wait & Non-Blocking Wait
accessing for
task.Result
causing the blocking wait that blocks the main threadcsTask<int> task = new(() => 123); task.Start(); Console.WriteLine(task.Result); // compiler knows it should wait the task blockingly // 123
1
2
3
4
5
6await
operator waits the task without blocking the calling thread.csint foo = await Foo(); // does not block the main thread but still synchronous static async Task<int> Foo { await Task.Delay(500); return 123; }
1
2
3
4
5
6task.Wait
: to wait the task itself.Task.Wait*
: utils to wait a batch of tasks in a blocking manner.Task.When*
: utils to wait a batch of tasks in a non-blocking manner.
A task must be started before awaiting, or the thread would be blocked forever. A task from async
method is started implicitly so no worry here.
To wait a task synchronously, use await
operator before a started task object.
var task = new Task(() => Console.WriteLine("hello"));
task.Start(); // must get it started!!!
await task;
2
3
Starting a simple task manually is quiet trivial so one should prefer Task.Run
or Task.Factory.StartNew
await Task.Factory.StartNew((object? foo) =>
{
Console.WriteLine(((dynamic)foo).Foo);
}, new { Foo = 345 });
await Task.Run(() => { Console.WriteLine("hello"); });
2
3
4
5
6
WhenAll
vs WhenAny
WhenAll
waits until all tasks were completed, returns all results in the order of tasks as an array.WhenAny
waits until one task was completed, returns the task completed first.
IMPORTANT
WhenAll
and WhenAny
do not start tasks for you, make sure they're started before waiting.
IEnumerable<Task<int>> tasks = Enumerable.Range(1, 10).Select(async x =>
{
await Task.Delay(x * 100);
return x;
});
int[] results = await Task.WhenAll(tasks); // 1, 2, 3 ... 10
Task<int> first = await Task.WhenAny(tasks);
Console.WriteLine(first.Result); // should be 1
2
3
4
5
6
7
8
9
Practical Usage
WhenAny
can be used as a simple countdown with Task.Delay
using CancellationTokenSource cts = new();
var token = cts.Token;
Task task = new Task(() =>
{
int i = Random.Shared.Next(1, 5); // random countdown
while (i-- > 0)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("operation...");
Thread.Sleep(1000);
}
}, token);
try
{
task.Start();
if (task == await Task.WhenAny(task, Task.Delay(3000))) // to race with the delay
{
Console.WriteLine("Task succeeded");
}
else
{
cts.Cancel();
Console.WriteLine("Task canceled for timeout");
}
}
catch (OperationCanceledException)
{
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29