Adding an anonymous Task to ListTask does not execute it after calling .WaitAll C
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When you create a Task using the new Task(...) constructor and add it to a List<Task>, calling Task.WaitAll() on the list hangs indefinitely. This happens because new Task(...) creates a task in the Created state — it has not been started. Task.WaitAll() waits for all tasks to complete, but a task that was never started will never complete. The fix is to use Task.Run() instead of new Task(), which creates and immediately schedules the task for execution.
The Problem
The new Task() constructor creates a "cold" task. It exists but is not scheduled on the thread pool. Task.WaitAll() waits for completion, creating a deadlock against tasks that will never run.
Fix 1: Use Task.Run() (Recommended)
Task.Run() creates a "hot" task — it is immediately scheduled on the thread pool and begins execution.
Fix 2: Call Start() After Creating
Calling Start() transitions the task from Created to WaitingToRun. This works but is error-prone — if you forget to call Start() on any task, WaitAll hangs.
Task States Explained
| Constructor | Initial State | Needs Start()? |
new Task(action) | Created | Yes |
Task.Run(action) | WaitingToRun | No |
Task.Factory.StartNew(action) | WaitingToRun | No |
Async/Await Pattern (Modern Approach)
Task.WhenAll() is the async equivalent of Task.WaitAll(). It returns a Task you can await without blocking the calling thread.
Task.Run with Return Values
Exception Handling with WaitAll
Task.WaitAll() collects all exceptions into a single AggregateException. Each faulted task's exception appears in InnerExceptions.
Common Pitfalls
- Using
new Task()instead ofTask.Run(): Thenew Task()constructor creates an unstarted task in theCreatedstate.Task.WaitAll()orawaiton this task hangs forever because it never runs. Always useTask.Run()for immediate execution. - Calling
Start()on an already-started task: CallingStart()on a task that was created withTask.Run()orTask.Factory.StartNew()throwsInvalidOperationException. Only cold tasks (fromnew Task()) can be started manually. - Using
Task.WaitAll()on the UI thread:WaitAll()blocks the calling thread synchronously. On a UI thread (WPF, WinForms), this freezes the application. Useawait Task.WhenAll()instead to keep the UI responsive. - Forgetting to handle
AggregateException:Task.WaitAll()wraps all task exceptions in anAggregateException. If you catch onlyException, you get the aggregate wrapper. AccessInnerExceptionsto handle each failure individually. - Adding async lambdas to
Task.Run()without awaiting:Task.Run(async () => { await SomeMethodAsync(); })returns aTaskthat wraps the async lambda. If you usenew Task(async () => ...), the task completes immediately because the constructor does not understandasyncdelegates — it sees the lambda as returningvoid.
Summary
new Task(action)creates a cold (unstarted) task —Task.WaitAll()hangs because the task never executes- Use
Task.Run(action)to create a hot (immediately scheduled) task - Prefer
await Task.WhenAll()overTask.WaitAll()to avoid blocking the calling thread - If you must use
new Task(), callStart()on every task before waiting - Handle
AggregateExceptionfromWaitAll()to catch exceptions from multiple tasks Task.Factory.StartNew()also creates hot tasks but has more configuration options thanTask.Run()

