
When working with asynchronous or parallel code in C#, you’ll inevitably encounter two common ways to start tasks: Task.Run and TaskFactory.StartNew. At first glance, they seem similar - but they behave differently and should be used appropriately depending on the context.
In this article, you’ll learn:
- What the difference is between
Task.RunandTaskFactory.StartNew - Which one you should prefer
- Code examples for both approaches
Task.Run
Task.Run was introduced in .NET 4.5 to simplify asynchronous programming. It wraps the more complex TaskFactory.StartNew with sensible defaults and is ideal for offloading CPU-bound work.
Example
1public Task RunWithTaskRunAsync()
2{
3 return Task.Run(() =>
4 {
5 // Simulated background work
6 Thread.Sleep(1000);
7 Console.WriteLine("Executed with Task.Run");
8 });
9}
Key Characteristics
- Uses TaskScheduler.Default (ThreadPool)
- Ideal for CPU-bound operations
- Automatically flows the current execution context (e.g., HttpContext, SecurityContext)
- Simple to use
TaskFactory.StartNew
TaskFactory.StartNew offers full control over task creation. You can configure schedulers, creation and continuation options - at the cost of increased complexity.
Simple Example
1public Task RunWithTaskFactoryAsync()
2{
3 TaskFactory factory = new TaskFactory();
4
5 return factory.StartNew(() =>
6 {
7 Thread.Sleep(1000);
8 Console.WriteLine("Executed with TaskFactory.StartNew");
9 });
10}
With Configuration Options
1public Task RunWithTaskFactoryOptionsAsync()
2{
3 TaskFactory factory = new TaskFactory(
4 CancellationToken.None,
5 TaskCreationOptions.DenyChildAttach,
6 TaskContinuationOptions.None,
7 TaskScheduler.Default);
8
9 return factory.StartNew(() =>
10 {
11 Thread.Sleep(1000);
12 Console.WriteLine("Executed with TaskFactory and options");
13 });
14}
Comparison Table
| Feature | Task.Run | TaskFactory.StartNew |
|---|---|---|
| Introduced In | .NET 4.5 | .NET 4.0 |
| Ease of Use | ✔ Simple | ⚠️ Complex |
| Context Flowing | ✔ Yes | ❌ No (must be configured manually) |
| Custom Scheduler Support | ❌ No | ✔ Yes |
| Async Lambda Friendly | ✔ Yes | ❌ No (requires care) |
| Configurable Options | ❌ No | ✔ Yes |
🟢 Recommendation
- ✔ Use
Task.Runfor offloading simple CPU-bound tasks in async code. - ❌ Avoid
TaskFactory.StartNewunless you need custom configuration, scheduling or advanced scenarios. - ⚠️ Do not use
TaskFactory.StartNewwithasynclambdas, unless you’re explicitly handling the returnedTask- it does not unwrap the async method properly.
Conclusion
In modern .NET applications, Task.Run is almost always the safer and cleaner choice. It supports async/await, ensures proper context handling and simplifies code.
Related articles

Mar 17, 2026 · 15 min read
GitHub Copilot - Custom Agents for Full-Stack Teams: A Practical Operating Model for .NET, React and Azure
GitHub Copilot custom agents allow teams to define specialized AI assistants, each with its own role, tool access and behavioral boundaries. …

Mar 10, 2026 · 15 min read
.NET NuGet Trusted Publishing with GitHub Actions
Publishing NuGet packages has traditionally required one uncomfortable compromise: a long-lived API key had to exist somewhere in the …

Mar 09, 2026 · 7 min read
C# 15 Unions: Unions are finally in .NET
After many years of workarounds, design discussions and library-level substitutes, unions are finally becoming a first-class part of C#. The …
Let's Work Together
Looking for an experienced Platform Architect or Engineer for your next project? Whether it's cloud migration, platform modernization or building new solutions from scratch - I'm here to help you succeed.

Comments