放弃Task.Run!这才是.NET异步编程的正确打开方式
|
admin
2025年6月18日 21:36
本文热度 1379
|
在.NET中进行异步编程时,许多开发者习惯使用Task.Run将工作卸载到线程池,但这往往不是最佳选择。本文将介绍.NET异步编程的正确方法,帮助你避免常见陷阱并充分发挥异步编程的优势。
关键要点总结
- 大多数.NET库都提供了异步版本的方法(如
File.ReadAllTextAsync、HttpClient.GetStringAsync) - 避免使用
Task.Run包装同步方法,这会增加线程池压力并可能导致性能下降
- 适用于I/O密集型操作(网络请求、文件读写、数据库查询等)
- 对于CPU密集型操作,
Task.Run可能仍然是合适的选择,但应谨慎使用
- 使用
await foreach处理大型数据集,避免内存溢出 - 结合EF Core的
AsAsyncEnumerable实现高效的数据处理
- 使用
SemaphoreSlim控制并发度,避免资源耗尽
- 使用
CancellationToken实现操作取消,提高响应性
- 在UI线程上永远不要阻塞(避免使用
.Result或.Wait())
遵循这些最佳实践,你可以编写出更高效、更可靠的异步代码,充分发挥.NET平台的异步编程能力,同时避免常见的陷阱和性能问题。
using System;using System.IO;using System.Net.Http;using System.Text.Json;using System.Threading;using System.Threading.Tasks;
public static class AsyncBestPractices{ public static async Task<string> DownloadFileAsync(string url) { using var client = new HttpClient(); return await client.GetStringAsync(url);
}
public static async Task ProcessFileAsync(string filePath) { await using var fileStream = new FileStream( filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);
using var reader = new StreamReader(fileStream); string content = await reader.ReadToEndAsync();
var processedContent = ProcessContent(content);
await using var outputStream = new FileStream( filePath + ".processed", FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true);
using var writer = new StreamWriter(outputStream); await writer.WriteAsync(processedContent); }
private static string ProcessContent(string content) { return content.ToUpper(); }
public static async Task<Order> GetOrderAsync(int orderId) { using var context = new OrderDbContext(); return await context.Orders.FindAsync(orderId); }
public static async Task ProcessOrdersAsync() { using var context = new OrderDbContext();
await foreach (var order in context.Orders.AsAsyncEnumerable()) { await ProcessOrderAsync(order); } }
private static async Task ProcessOrderAsync(Order order) { await Task.Delay(10); Console.WriteLine($"处理订单: {order.Id}"); }
public static async Task DownloadMultipleFilesAsync(string[] urls) { var downloadTasks = urls.Select(url => DownloadAndSaveFileAsync(url));
await Task.WhenAll(downloadTasks); }
private static async Task DownloadAndSaveFileAsync(string url) { using var client = new HttpClient(); var content = await client.GetStringAsync(url);
var fileName = Path.GetFileName(url); await File.WriteAllTextAsync(fileName, content); }
public static async Task SafeDownloadAsync(string url) { try { using var client = new HttpClient(); var content = await client.GetStringAsync(url); await ProcessDownloadedContentAsync(content); } catch (HttpRequestException ex) { Console.WriteLine($"下载失败: {ex.Message}"); } catch (OperationCanceledException) { Console.WriteLine("操作已取消"); } catch (Exception ex) { Console.WriteLine($"发生未知错误: {ex.Message}"); } }
private static Task ProcessDownloadedContentAsync(string content) { return Task.CompletedTask; }
public static async Task CancelableOperationAsync(CancellationToken cancellationToken) { using var client = new HttpClient();
try { var response = await client.GetAsync("https://example.com", cancellationToken);
if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(cancellationToken); Console.WriteLine($"下载完成: {content.Length} 字符"); } } catch (OperationCanceledException) { Console.WriteLine("操作被用户取消"); } }
public static async void ButtonClickHandler(object sender, EventArgs e) {
await LongRunningOperationAsync(); }
private static async Task LongRunningOperationAsync() { await Task.Delay(5000); Console.WriteLine("操作完成"); }
public static async IAsyncEnumerable<string> ReadLinesAsync(string filePath) { await using var fileStream = new FileStream( filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);
using var reader = new StreamReader(fileStream);
string line; while ((line = await reader.ReadLineAsync()) != null) { yield return line; } }
public static async Task ProcessItemsWithThrottleAsync(IEnumerable<string> items, int maxConcurrency = 5) { var semaphore = new SemaphoreSlim(maxConcurrency);
var tasks = items.Select(async item => { await semaphore.WaitAsync();
try { await ProcessItemAsync(item); } finally { semaphore.Release(); } });
await Task.WhenAll(tasks); }
private static async Task ProcessItemAsync(string item) { await Task.Delay(100); Console.WriteLine($"处理项: {item}"); }}
public class OrderDbContext : DbContext{ public DbSet<Order> Orders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("YourConnectionString"); }}
public class Order{ public int Id { get; set; } public string CustomerName { get; set; } public DateTime OrderDate { get; set; }}
阅读原文:原文链接
该文章在 2025/6/19 18:20:16 编辑过