放弃Task.Run!这才是.NET异步编程的正确打开方式
|
admin
2025年6月18日 21:36
本文热度 28
|
在.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 编辑过