基础教程中我们学会了 async/await 的基本用法。但在高性能场景下,不当的异步编程会导致性能反而比同步更差。本文将深入探讨 ValueTask、IAsyncEnumerable、Channel、管道模式、以及异步同步上下文等高级话题,帮助你编写真正高效的异步代码。
ValueTask —— 减少异步分配 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public async Task <int > GetCachedValueAsync (){ if (_cache.TryGetValue("key" , out int value )) return value ; return await FetchFromDbAsync(); } public ValueTask <int > GetCachedValueOptimizedAsync (){ if (_cache.TryGetValue("key" , out int value )) return new ValueTask<int >(value ); return new ValueTask<int >(FetchFromDbAsync()); }
何时使用 ValueTask
场景
推荐
方法经常同步完成(如缓存命中)
✅ ValueTask
热路径调用(每秒数万次)
✅ ValueTask
需要多次 await
❌ Task
需要 Task.WhenAll
❌ Task
IAsyncEnumerable —— 异步流 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 async IAsyncEnumerable<int > ProduceNumbersAsync ( [EnumeratorCancellation] CancellationToken token = default ){ for (int i = 0 ; i < 10 ; i++) { await Task.Delay(100 , token); yield return i; } } await foreach (var number in ProduceNumbersAsync ()){ Console.WriteLine(number); } using var cts = new CancellationTokenSource(500 );await foreach (var number in ProduceNumbersAsync (cts.Token )){ Console.WriteLine(number); }
实际应用:分页数据加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 async IAsyncEnumerable<Order> GetOrdersPagedAsync ( int pageSize = 100 , [EnumeratorCancellation] CancellationToken token = default ){ int page = 0 ; bool hasMore = true ; while (hasMore) { token.ThrowIfCancellationRequested(); var pageData = await FetchPageAsync(page++, pageSize, token); foreach (var order in pageData) yield return order; hasMore = pageData.Count == pageSize; } } await foreach (var order in GetOrdersPagedAsync ().Take (500 )){ Process(order); }
异步 LINQ(System.Linq.Async) 1 2 3 4 5 6 7 8 using System.Linq;var result = await GetOrdersPagedAsync() .WhereAwait(async order => await ValidateOrderAsync(order)) .Select(order => new { order.Id, order.Amount }) .ToListAsync();
Channel —— 生产者-消费者进阶 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using System.Threading.Channels;var options = new BoundedChannelOptions(100 ){ FullMode = BoundedChannelFullMode.Wait, SingleWriter = false , SingleReader = false , }; var channel = Channel.CreateBounded<Order>(options);var unbounded = Channel.CreateUnbounded<string >(new UnboundedChannelOptions{ SingleWriter = true , SingleReader = true , }); async Task BatchConsumerAsync (ChannelReader<Order> reader, int batchSize = 10 ){ var buffer = new List<Order>(batchSize); await foreach (var order in reader.ReadAllAsync()) { buffer.Add(order); if (buffer.Count >= batchSize) { await ProcessBatchAsync(buffer); buffer.Clear(); } } if (buffer.Count > 0 ) await ProcessBatchAsync(buffer); }
管道模式(Pipeline) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class ImagePipeline { private readonly Channel<string > _download = Channel.CreateBounded<string >(10 ); private readonly Channel<byte []> _process = Channel.CreateBounded<byte []>(10 ); private readonly Channel<string > _save = Channel.CreateBounded<string >(10 ); public async Task StartAsync (string [] urls ) { var downloader = DownloadAsync(); var processor = ProcessAsync(); var saver = SaveAsync(); foreach (var url in urls) await _download.Writer.WriteAsync(url); _download.Writer.Complete(); await Task.WhenAll(downloader, processor, saver); } private async Task DownloadAsync () { await foreach (var url in _download.Reader.ReadAllAsync()) { var data = await new HttpClient().GetByteArrayAsync(url); await _process.Writer.WriteAsync(data); } _process.Writer.Complete(); } private async Task ProcessAsync () { await foreach (var data in _process.Reader.ReadAllAsync()) { var processed = await ProcessImageDataAsync(data); await _save.Writer.WriteAsync($"processed_{Guid.NewGuid()} .jpg" ); } _save.Writer.Complete(); } private async Task SaveAsync () { await foreach (var name in _save.Reader.ReadAllAsync()) { Console.WriteLine($"保存: {name} " ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public async Task<string > ReadFileAsync (string path ){ using var file = File.OpenText(path); string content = await file.ReadToEndAsync().ConfigureAwait(false ); return content; }
异步锁(SemaphoreSlim) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class AsyncResource { private readonly SemaphoreSlim _semaphore = new (1 , 1 ); public async Task AccessAsync () { await _semaphore.WaitAsync(); try { await Task.Delay(100 ); Console.WriteLine("异步临界区" ); } finally { _semaphore.Release(); } } } public sealed class AsyncLock { private readonly SemaphoreSlim _semaphore = new (1 , 1 ); public async Task<IDisposable> LockAsync () { await _semaphore.WaitAsync(); return new Releaser(this ); } private sealed class Releaser : IDisposable { private readonly AsyncLock _lock; public Releaser (AsyncLock l ) => _lock = l; public void Dispose () => _lock._semaphore.Release(); } } private readonly AsyncLock _lock = new ();async Task SafeAccessAsync (){ using await _lock.LockAsync(); }
异步初始化与缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class AsyncInitializedService { private readonly Lazy<Task<ExpensiveResource>> _resource; public AsyncInitializedService () { _resource = new Lazy<Task<ExpensiveResource>>( () => InitializeAsync(), LazyThreadSafetyMode.ExecutionAndPublication); } public async Task<ExpensiveResource> GetResourceAsync () => await _resource.Value; private async Task<ExpensiveResource> InitializeAsync () { await Task.Delay(1000 ); return new ExpensiveResource(); } } public class AsyncCache <TKey , TValue > where TKey : notnull { private readonly ConcurrentDictionary<TKey, Task<TValue>> _cache = new (); private readonly Func<TKey, Task<TValue>> _factory; public AsyncCache (Func<TKey, Task<TValue>> factory ) => _factory = factory; public Task<TValue> GetOrAddAsync (TKey key ) { return _cache.GetOrAdd(key, (k, fac) => fac(k), _factory); } }
超时与取消模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public async Task <T > WithTimeout <T >(Task<T> task, TimeSpan timeout ){ using var cts = new CancellationTokenSource(timeout); var completed = await Task.WhenAny(task, Task.Delay(timeout, cts.Token)); if (completed == task) return await task; throw new TimeoutException($"操作超时 ({timeout.TotalSeconds} s)" ); } public async Task ProcessAsync (CancellationToken externalToken ){ using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(30 )); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( externalToken, timeoutCts.Token); await LongOperationAsync(linkedCts.Token); }
异步性能基准
总结 :高级异步编程的核心是理解”分配”和”调度”。ValueTask 消除热路径的堆分配,Channel 提供高性能的生产者-消费者通信,ConfigureAwait(false) 避免死锁,而管道模式则让复杂的工作流变得清晰可控。掌握这些模式后,你的异步代码不仅能正确运行,更能以最少的资源消耗实现最大的吞吐量。