Task.WhenAny for first completion in C Sharp (C#) - Time & Space Complexity
We want to understand how the time to get the first finished task grows as we add more tasks.
How does waiting for the first task to complete change when we have more tasks running?
Analyze the time complexity of the following code snippet.
var tasks = new List<Task>();
for (int i = 0; i < n; i++)
{
tasks.Add(Task.Run(() => DoWork()));
}
var firstFinished = await Task.WhenAny(tasks);
await firstFinished;
This code starts n tasks and waits for the first one to finish.
- Primary operation: Starting n tasks in a loop.
- How many times: n times, once per task.
- Waiting operation: Waiting for the first task to complete happens once.
Starting tasks grows linearly with n, but waiting for the first to finish depends on the fastest task.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Start 10 tasks, wait for 1 to finish |
| 100 | Start 100 tasks, wait for 1 to finish |
| 1000 | Start 1000 tasks, wait for 1 to finish |
Pattern observation: Starting tasks grows with n, but waiting time depends on the quickest task, not all.
Time Complexity: O(n)
This means the work to start tasks grows with the number of tasks, but waiting stops as soon as the first finishes.
[X] Wrong: "Waiting for the first task to finish takes as long as waiting for all tasks."
[OK] Correct: We only wait for the fastest task, so the wait time does not add up for all tasks.
Understanding how waiting for the first task completion scales helps you reason about asynchronous code efficiency in real projects.
"What if we changed Task.WhenAny to Task.WhenAll? How would the time complexity change?"
Practice
Task.WhenAny do in C#?Solution
Step 1: Understand Task.WhenAny purpose
Task.WhenAny waits for the first task to finish among many tasks.Step 2: Compare with other options
Unlike Task.WhenAll, it does not wait for all tasks, nor does it cancel tasks or create infinite tasks.Final Answer:
It waits for the first task to complete among multiple tasks. -> Option DQuick Check:
Task.WhenAny = first task completion [OK]
- Confusing Task.WhenAny with Task.WhenAll
- Thinking it cancels other tasks
- Assuming it waits for all tasks
Task.WhenAny with two tasks task1 and task2?Solution
Step 1: Check correct method call syntax
Task.WhenAny accepts params array of tasks, so calling with (task1, task2) is correct.Step 2: Identify invalid syntax
Square brackets, curly braces, or missing parentheses cause syntax errors.Final Answer:
var first = await Task.WhenAny(task1, task2); -> Option AQuick Check:
Correct method call uses parentheses and comma-separated tasks [OK]
- Using square brackets instead of parentheses
- Using curly braces instead of parentheses
- Omitting parentheses around arguments
var task1 = Task.Delay(3000).ContinueWith(_ => "Task1 done"); var task2 = Task.Delay(1000).ContinueWith(_ => "Task2 done"); var first = await Task.WhenAny(task1, task2); Console.WriteLine(await first);
Solution
Step 1: Analyze task delays
task1 completes after 3000ms, task2 after 1000ms, so task2 finishes first.Step 2: Task.WhenAny returns first completed task
first will be task2, so awaiting it prints "Task2 done".Final Answer:
Task2 done -> Option AQuick Check:
First completed task result = Task2 done [OK]
- Assuming task1 finishes first because it is listed first
- Expecting both tasks to print
- Thinking program hangs waiting for all tasks
Task.WhenAny:
var task1 = Task.Delay(2000); var task2 = Task.Delay(1000); var first = Task.WhenAny(task1, task2); Console.WriteLine(await first.Result);
Solution
Step 1: Check how Task.WhenAny is awaited
Task.WhenAny returns a Task<Task>, so it must be awaited to get the completed task.Step 2: Identify misuse of Result property
Accessing first.Result without awaiting first causes incorrect behavior or deadlock.Final Answer:
Task.WhenAny should be awaited before accessing Result. -> Option BQuick Check:
Await Task.WhenAny before accessing completed task [OK]
- Accessing Result property without await
- Confusing Task<Task> with Task
- Ignoring async/await pattern
taskA (2s delay), taskB (1s delay), and taskC (3s delay). You want to print the result of the first completed task and then cancel the other two tasks. Which code snippet correctly achieves this?Solution
Step 1: Use CancellationTokenSource to cancel tasks
Create a CancellationTokenSource and pass its token to all tasks so they can be cancelled.Step 2: Await Task.WhenAny and cancel remaining tasks
Await Task.WhenAny to get first completed task, then call cts.Cancel() to stop others, finally print the result.Final Answer:
Use CancellationTokenSource, await Task.WhenAny, then cancel and print. -> Option CQuick Check:
Cancel tasks after first completion using CancellationTokenSource [OK]
- Not passing cancellation token to tasks
- Calling Cancel before awaiting Task.WhenAny
- Using .Result instead of await causing deadlocks
Ready to prove it?
Pass the test and this pattern moves unknown
