Task.Run vs. TaskFactory.StartNew
๐งต Task.Run vs. TaskFactory.StartNew in C#
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.Run
andTaskFactory.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
public Task RunWithTaskRunAsync()
{
return Task.Run(() =>
{
// Simulated background work
Thread.Sleep(1000);
Console.WriteLine("Executed with Task.Run");
});
}
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
public Task RunWithTaskFactoryAsync()
{
TaskFactory factory = new TaskFactory();
return factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Executed with TaskFactory.StartNew");
});
}
With Configuration Options
public Task RunWithTaskFactoryOptionsAsync()
{
TaskFactory factory = new TaskFactory(
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskContinuationOptions.None,
TaskScheduler.Default);
return factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Executed with TaskFactory and options");
});
}
๐ 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.Run
for offloading simple CPU-bound tasks in async code. - โ Avoid
TaskFactory.StartNew
unless you need custom configuration, scheduling, or advanced scenarios. - โ ๏ธ Do not use
TaskFactory.StartNew
withasync
lambdas, 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.