System.Threading.Tasks.Extensions
是一个重要的 .NET 库,它为 System.Threading.Tasks
命名空间提供了扩展功能,主要用于增强任务并行库(Task Parallel Library, TPL)的功能。
设计原理
1. 核心目标
提供对
ValueTask
和ValueTask<T>
的支持,减少异步操作中的堆分配扩展任务相关的API,提供更灵活的异步编程模式
桥接同步和异步编程模型
2. 关键组件
ValueTask/ValueTask<T>:轻量级任务结构,避免在热路径中分配Task对象
TaskCompletionSource 扩展:支持更多任务控制场景
IValueTaskSource:允许自定义ValueTask行为
3. 性能考虑
减少GC压力:通过值类型任务减少堆分配
热路径优化:为高频调用的异步操作提供更高效实现
同步完成优化:对于可以同步完成的操作避免状态机开销
主要用法
1. ValueTask 基础用法
public ValueTask<int> GetDataAsync()
{
if (_cache.TryGetValue(key, out var data))
{
return new ValueTask<int>(data); // 同步完成
}
return new ValueTask<int>(LoadFromDatabaseAsync()); // 异步完成
}
2. 自定义 IValueTaskSource
public class CustomTaskSource : IValueTaskSource<int>
{
public int GetResult(short token) => /* 结果 */;
public ValueTaskSourceStatus GetStatus(short token) => /* 状态 */;
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
=> /* 完成回调 */;
}
public ValueTask<int> GetCustomDataAsync()
{
var source = new CustomTaskSource();
return new ValueTask<int>(source, token: 0);
}
3. 任务扩展方法
// 配置任务等待方式
await someTask.ConfigureAwait(false);
// 带超时的等待
await someTask.WaitAsync(TimeSpan.FromSeconds(5));
三个常见示例
示例1:高性能异步方法(减少分配)
public ValueTask<string> ReadFileAsync(string path)
{
if (_memoryCache.TryGetValue(path, out string content))
{
return new ValueTask<string>(content); // 同步返回,无分配
}
return new ValueTask<string>(File.ReadAllTextAsync(path)); // 异步路径
}
应用场景:高频调用的缓存读取操作,大多数情况下可以从缓存同步返回。
示例2:自定义异步操作
public class TimerDelaySource : IValueTaskSource<bool>
{
private readonly Timer _timer;
private readonly CancellationToken _cancellationToken;
private Action<object> _continuation;
public TimerDelaySource(TimeSpan delay, CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
_timer = new Timer(_ => OnTimer(), null, delay, Timeout.InfiniteTimeSpan);
cancellationToken.Register(() => OnTimer());
}
private void OnTimer()
{
var cont = Interlocked.Exchange(ref _continuation, null);
cont?.Invoke(this);
}
public bool GetResult(short token) => !_cancellationToken.IsCancellationRequested;
public ValueTaskSourceStatus GetStatus(short token)
=> _continuation == null ? ValueTaskSourceStatus.Succeeded : ValueTaskSourceStatus.Pending;
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
_continuation = continuation;
}
}
public static ValueTask<bool> DelayAsync(TimeSpan delay, CancellationToken cancellationToken = default)
{
return new ValueTask<bool>(new TimerDelaySource(delay, cancellationToken), 0);
}
应用场景:需要完全控制异步操作实现细节的高性能场景。
示例3:带超时的异步操作
public static async ValueTask<string> ReadWithTimeoutAsync(Stream stream, byte[] buffer, TimeSpan timeout)
{
using var cts = new CancellationTokenSource(timeout);
try
{
var readTask = stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);
return await readTask.ConfigureAwait(false);
}
catch (OperationCanceledException) when (cts.IsCancellationRequested)
{
throw new TimeoutException("读取操作超时");
}
}
应用场景:网络请求或IO操作需要设置超时限制的情况。
最佳实践
优先使用ValueTask:当方法可能频繁同步完成时
避免多次await:ValueTask通常只能被await一次
注意对象池:对于自定义IValueTaskSource,考虑实现对象池
正确配置上下文:合理使用ConfigureAwait
性能考虑
同步完成路径:ValueTask在同步完成时性能显著优于Task
异步路径:两者性能相近,ValueTask稍慢(因结构体包装)
内存占用:ValueTask减少GC压力,适合高频调用场景
这个库在现代.NET异步编程中扮演着重要角色,特别是在性能敏感的场景下,理解其设计原理和正确用法可以帮助开发者编写更高效的异步代码。