What is the difference between `Task.FromResult()` and `Task.Run()`?
Quick Answer
`Task.FromResult()` synchronously wraps an already-available value in a completed task — no thread is used, ideal for returning cached results or implementing async interfaces synchronously. `Task.Run()` queues a delegate to run on a thread-pool thread, so it's for offloading CPU-bound work. Using `Task.Run` to return a known value (or to make I/O 'async') is wasteful — prefer `FromResult`/true async APIs.
Detailed Answer
Task.FromResult() creates a completed task with a result value, while Task.Run() queues work to run on the ThreadPool. The key difference is that Task.FromResult() is synchronous and immediate, while Task.Run() is asynchronous and offloads work to a background thread.
Key Differences:
| Aspect | Task.FromResult() | Task.Run() |
|---|---|---|
| Execution | Synchronous, immediate | Asynchronous, queued |
| Thread | Runs on current thread | Runs on ThreadPool thread |
| Use Case | Already computed values | CPU-bound work |
| Performance | No overhead | Thread switching overhead |
| When to Use | Converting sync to async API | Offloading CPU work |
Example:
public class TaskCreationComparison
{
// Task.FromResult() - for already computed values
public async Task<string> GetCachedDataAsync(string key)
{
// Simulate cache lookup (synchronous operation)
string cachedValue = GetFromCache(key);
if (cachedValue != null)
{
// Already have the value - use Task.FromResult()
return await Task.FromResult(cachedValue);
}
// Need to fetch from database (async operation)
return await FetchFromDatabaseAsync(key);
}
// Task.Run() - for CPU-bound work
public async Task<int> CalculatePrimeCountAsync(int maxNumber)
{
// CPU-intensive work - offload to ThreadPool
return await Task.Run(() =>
{
int count = 0;
for (int i = 2; i <= maxNumber; i++)
{
if (IsPrime(i))
count++;
}
return count;
});
}
// WRONG: Using Task.Run() for already computed values
public async Task<string> GetCachedDataWrongAsync(string key)
{
string cachedValue = GetFromCache(key);
if (cachedValue != null)
{
// BAD: Unnecessary thread switching overhead
return await Task.Run(() => cachedValue);
}
return await FetchFromDatabaseAsync(key);
}
// WRONG: Using Task.FromResult() for CPU-bound work
public async Task<int> CalculatePrimeCountWrongAsync(int maxNumber)
{
// BAD: Blocks the current thread
int count = 0;
for (int i = 2; i <= maxNumber; i++)
{
if (IsPrime(i))
count++;
}
return await Task.FromResult(count);
}
private string GetFromCache(string key)
{
// Simulate cache lookup
return key == "cached" ? "cached_value" : null;
}
private async Task<string> FetchFromDatabaseAsync(string key)
{
await Task.Delay(1000); // Simulate database call
return $"database_value_for_{key}";
}
private bool IsPrime(int number)
{
if (number < 2) return false;
for (int i = 2; i * i <= number; i++)
{
if (number % i == 0) return false;
}
return true;
}
}
Advanced Examples:
public class AdvancedTaskCreation
{
// Task.FromResult() for configuration values
public async Task<AppSettings> GetAppSettingsAsync()
{
// Configuration is already loaded - no need for async
var settings = LoadConfiguration();
return await Task.FromResult(settings);
}
// Task.FromResult() for constants
public async Task<string> GetApiVersionAsync()
{
return await Task.FromResult("v1.0");
}
// Task.FromResult() for simple calculations
public async Task<decimal> CalculateTaxAsync(decimal amount, decimal rate)
{
decimal tax = amount * rate;
return await Task.FromResult(tax);
}
// Task.Run() for file processing
public async Task<string> ProcessLargeFileAsync(string filePath)
{
return await Task.Run(() =>
{
// CPU-intensive file processing
var lines = File.ReadAllLines(filePath);
var processedLines = lines
.Where(line => !string.IsNullOrWhiteSpace(line))
.Select(line => line.ToUpper())
.OrderBy(line => line)
.ToArray();
return string.Join("\n", processedLines);
});
}
// Task.Run() for image processing
public async Task<byte[]> ResizeImageAsync(byte[] imageData, int width, int height)
{
return await Task.Run(() =>
{
// CPU-intensive image processing
using (var originalImage = Image.FromStream(new MemoryStream(imageData)))
using (var resizedImage = new Bitmap(originalImage, width, height))
using (var stream = new MemoryStream())
{
resizedImage.Save(stream, ImageFormat.Jpeg);
return stream.ToArray();
}
});
}
private AppSettings LoadConfiguration()
{
// Simulate configuration loading
return new AppSettings { DatabaseConnection = "Server=localhost", ApiKey = "secret" };
}
}
public class AppSettings
{
public string DatabaseConnection { get; set; }
public string ApiKey { get; set; }
}
Performance Comparison:
public class PerformanceComparison
{
public async Task ComparePerformance()
{
const int iterations = 10000;
// Task.FromResult() - very fast
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
await Task.FromResult(i);
}
stopwatch.Stop();
Console.WriteLine($"Task.FromResult(): {stopwatch.ElapsedMilliseconds}ms");
// Task.Run() - slower due to thread switching
stopwatch.Restart();
for (int i = 0; i < iterations; i++)
{
await Task.Run(() => i);
}
stopwatch.Stop();
Console.WriteLine($"Task.Run(): {stopwatch.ElapsedMilliseconds}ms");
}
}
Best Practices:
Use Task.FromResult() when:
- You already have the computed value
- Converting synchronous APIs to async
- Returning constants or configuration values
- Simple calculations that don't block
Use Task.Run() when:
- Performing CPU-intensive work
- Processing large files or data
- Image/video processing
- Mathematical calculations
- Any work that could block the UI thread
Avoid Task.Run() when:
- You already have the result
- The work is already asynchronous
- You're just wrapping synchronous I/O operations
- The operation is very fast
Common Anti-patterns:
// BAD: Unnecessary Task.Run()
public async Task<string> GetUserNameAsync(int userId)
{
var user = await GetUserFromDatabaseAsync(userId);
return await Task.Run(() => user.Name); // Unnecessary!
}
// GOOD: Use Task.FromResult() or just return directly
public async Task<string> GetUserNameAsync(int userId)
{
var user = await GetUserFromDatabaseAsync(userId);
return user.Name; // Simple return
}
// BAD: Blocking with Task.FromResult()
public async Task<string> ProcessDataAsync(string data)
{
var result = ExpensiveProcessing(data); // Blocks current thread
return await Task.FromResult(result);
}
// GOOD: Use Task.Run() for CPU work
public async Task<string> ProcessDataAsync(string data)
{
return await Task.Run(() => ExpensiveProcessing(data));
}
Summary:
Task.FromResult(): For already computed values, no thread switchingTask.Run(): For CPU-bound work that needs to run on background thread- Choose based on whether you need to offload work or just return a value
- Performance matters: avoid unnecessary thread switching