What is `ValueTask` and when should you use it over `Task`?
4 minadvanced.NETValueTaskperformanceasync-await
Quick Answer
`ValueTask`/`ValueTask<T>` is a struct-based alternative to `Task` that avoids a heap allocation when an async method frequently completes synchronously (e.g., cached results). It reduces GC pressure in hot paths but has strict rules: don't await it more than once, don't block on it, and don't use it concurrently. Use `Task` by default and `ValueTask` only for performance-critical APIs that often complete synchronously.
Detailed Answer
ValueTask is a value type (struct) that represents an asynchronous operation, introduced to reduce heap allocations in high-performance scenarios.
Key differences from Task:
Task:
- Reference type (class) - allocated on the heap
- Can be awaited multiple times
- Can be cached and reused safely
- Slightly more overhead due to allocation
ValueTask:
- Value type (struct) - can be allocated on the stack
- Should only be awaited once
- More efficient when the operation completes synchronously
- Less garbage collection pressure
When to use ValueTask:
Use ValueTask when:
- The operation frequently completes synchronously (e.g., cached results)
- Performance is critical and you want to avoid allocations
- You're building high-throughput libraries or APIs
- The result is only awaited once
// Good use case: Cache-backed operation
public async ValueTask GetUserAsync(int userId)
{
// Check cache first - often completes synchronously
if (_cache.TryGetValue(userId, out var user))
{
return user; // No Task allocation needed
}
// Cache miss - fetch from database
user = await _database.GetUserAsync(userId);
_cache.Add(userId, user);
return user;
}
When to use Task:
Use Task when:
- The operation is always asynchronous
- You need to await the result multiple times
- You need to store the task for later use
- Working with public APIs where simplicity matters
// Task is better here - result may be awaited multiple times
public Task FetchDataAsync()
{
return _httpClient.GetStringAsync("https://api.example.com/data");
}
Important rules for ValueTask:
- Only await a ValueTask once
- Don't store ValueTask in fields or properties
- Convert to Task if you need multiple awaits:
valueTask.AsTask()