Task.Factory.StartNew followed by Task.Wait
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Task.Factory.StartNew() starts work asynchronously, but calling Task.Wait() immediately after it often removes most of the benefit by blocking the current thread until the task completes. In modern C#, this pattern usually signals that the code should use await, or that it may not need a task at all.
What the pattern actually does
Consider this code:
StartNew queues the delegate for asynchronous execution. Wait then blocks the calling thread until that task finishes. The result is concurrency at the task level, but not responsiveness at the caller level.
If the caller is a UI thread or an ASP.NET request thread, that blocking can be exactly what you were trying to avoid in the first place.
Why Wait() is often the wrong follow-up
Wait() is a synchronous blocking call. That means:
- the current thread is stuck until the task completes
- exceptions are wrapped in
AggregateException - UI and request threads can become unresponsive
In older Task Parallel Library code, blocking was common. In modern async code, it is usually better to let the method remain asynchronous and use await.
Here, the caller does not block the thread while waiting for completion.
Prefer Task.Run for simple background work
Task.Factory.StartNew exposes a lot of configuration knobs, but that flexibility also makes it easier to misuse. For straightforward background work, Task.Run is usually the clearer API.
Task.Run expresses the common case directly: queue work to the thread pool and await the result.
When blocking is acceptable
There are cases where Wait() is still reasonable:
- console app entry points in older code
- test harnesses
- compatibility layers around synchronous APIs
Even then, you should understand the tradeoff. Blocking is not free, and it changes how exceptions and cancellation behave.
For example:
With await, the original exception is usually easier to handle.
Why StartNew can surprise people
Task.Factory.StartNew predates async and await, and it has semantics that are easy to misuse. For example, if you pass it an async delegate, you can end up with nested tasks.
That is one reason Task.Run is preferred for most modern async code. It handles common cases more predictably.
A better mental model
Choose the pattern based on what you want:
- if work is naturally asynchronous, keep the whole path async and use
await - if you need thread-pool offloading for CPU work, use
Task.Run - if you block immediately with
Wait, ask whether you needed a task at all
For example, if code is purely synchronous and you will wait right away, a direct method call may be simpler:
Creating a task only to block on it can add overhead and complexity without improving throughput or readability.
Common Pitfalls
The biggest mistake is calling Wait() on a UI or request thread. That can freeze the interface or reduce server scalability, and in some environments it can contribute to deadlock patterns.
Another issue is choosing Task.Factory.StartNew by habit when Task.Run is the simpler and safer option. StartNew is powerful, but it is not the default best practice for routine async work.
Developers also forget that Wait() changes exception handling. Instead of seeing the original exception directly, you often get AggregateException, which adds friction for no real gain.
Finally, a task that is created and then immediately waited on may indicate the code path does not need asynchronous structure at all. Sometimes the cleanest fix is to remove the task wrapper.
Summary
- '
StartNewbegins asynchronous work, whileWait()blocks the current thread for completion.' - Calling
Wait()immediately often defeats the responsiveness benefits of task-based code. - Prefer
awaitandTask.Runin modern C# unless you have a specific reason to block. - Blocking calls can hurt UI responsiveness, server scalability, and exception clarity.
- If you always wait immediately, reconsider whether a task is necessary at all.
Related reading
- Task.Factory.StartNew vs Task.Factory.FromAsync
- Task.Run and UI Progress Updates
- Task.StartOrRestartWhenPossible
- Task.WaitAll is not waiting - Explanation
- Task.WhenAll - does it create a new thread?
- Task.WhenAll not throwing exception as expected
- Task.WhenAll vs Parallel.ForEachAsync - Which approach is best and why?
- TcpClient vs Socket when dealing with asynchronousy

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.