C# 异步详解

C# 异步编程详解

一、异步编程基础概念

1. 同步 vs 异步

  • ​同步(Synchronous)​​:任务按顺序执行,前一个任务完成后才会执行下一个
  • ​异步(Asynchronous)​​:任务可以非阻塞地启动,主线程可以继续执行其他操作

2. 异步编程的优势

  • 提高应用程序响应能力(特别是UI应用)
  • 更好地利用系统资源
  • 避免线程阻塞
  • 提高吞吐量

二、C#异步编程模型

1. async/await关键字

public async Task<string> GetDataAsync()
{
    // 模拟耗时操作
    await Task.Delay(1000);
    return "Data loaded";
}

// 使用
var data = await GetDataAsync();
Console.WriteLine(data);

​关键点​​:

  • async修饰方法,表示该方法包含异步操作
  • await关键字用于等待异步操作完成
  • 异步方法返回TaskTask<T>

2. Task和Task

// 返回void的异步方法(仅用于事件处理)
public async void HandleButtonClick()
{
    await SomeAsyncOperation();
}

// 返回Task的异步方法
public async Task ProcessDataAsync()
{
    await File.ReadAllTextAsync("data.txt");
}

// 返回Task<T>的异步方法
public async Task<int> CalculateSumAsync(IEnumerable<int> numbers)
{
    return await Task.Run(() => numbers.Sum());
}

​Task状态​​:

  • Created:已创建但未启动
  • WaitingForActivation:等待激活
  • WaitingToRun:等待运行
  • Running:正在运行
  • RanToCompletion:已完成
  • Canceled:已取消
  • Faulted:出错

三、异步方法实现方式

1. 基于I/O的异步操作

// 文件I/O
public async Task ReadFileAsync(string path)
{
    using (var reader = File.OpenText(path))
    {
        var content = await reader.ReadToEndAsync();
        Console.WriteLine(content);
    }
}

// 网络I/O
public async Task DownloadDataAsync(string url)
{
    using (var client = new HttpClient())
    {
        var response = await client.GetStringAsync(url);
        Console.WriteLine(response);
    }
}

2. 基于CPU的异步操作

// 使用Task.Run将CPU密集型工作转移到线程池
public async Task ProcessDataAsync(IEnumerable<int> data)
{
    var result = await Task.Run(() =>
    {
        // CPU密集型计算
        return data.Sum(x => x * x);
    });
    
    Console.WriteLine($"Sum: {result}");
}

​注意​​:对于真正的并行计算,考虑使用Parallel.For或PLINQ

3. 组合多个异步操作

// 等待多个任务完成
public async Task ProcessMultipleAsync()
{
    var task1 = GetDataAsync();
    var task2 = GetOtherDataAsync();
    
    // 等待所有完成
    await Task.WhenAll(task1, task2);
    
    Console.WriteLine($"Data1: {task1.Result}, Data2: {task2.Result}");
}

// 等待任意一个完成
public async Task ProcessAnyAsync()
{
    var task1 = GetDataAsync();
    var task2 = GetOtherDataAsync();
    
    var completedTask = await Task.WhenAny(task1, task2);
    Console.WriteLine($"Completed: {completedTask == task1 ? "Task1" : "Task2"}");
}

四、异步编程最佳实践

1. 避免async void

// 不推荐 - 无法捕获异常
public async void DangerousMethod()
{
    throw new Exception("Oops!");
}

// 推荐 - 可以await或等待
public async Task SafeMethodAsync()
{
    await Task.Delay(100);
    throw new Exception("Oops!");
}

​例外​​:事件处理程序可以使用async void

2. 异常处理

public async Task HandleExceptionsAsync()
{
    try
    {
        await SomeAsyncOperation();
    }
    catch (Exception ex)
    {
        // 处理异常
        Console.WriteLine($"Error: {ex.Message}");
    }
}

// 多个任务的异常处理
public async Task HandleMultipleExceptionsAsync()
{
    try
    {
        await Task.WhenAll(
            Operation1(),
            Operation2(),
            Operation3());
    }
    catch (AggregateException ae)
    {
        foreach (var ex in ae.InnerExceptions)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

3. 取消异步操作

// 使用CancellationToken
public async Task LongRunningOperationAsync(CancellationToken token)
{
    for (int i = 0; i < 100; i++)
    {
        token.ThrowIfCancellationRequested();
        await Task.Delay(100, token);
        Console.WriteLine($"Step {i}");
    }
}

// 使用示例
var cts = new CancellationTokenSource();
try
{
    await LongRunningOperationAsync(cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was canceled");
}

// 取消操作
cts.Cancel();

4. 异步方法设计原则

  1. ​返回Task而非void​​(除非是事件处理程序)
  2. ​避免在异步方法中使用Result或Wait()​​(可能导致死锁)
  3. ​保持异步方法链​​(避免混合同步和异步代码)
  4. ​考虑异步方法的粒度​​(不要过度拆分)

五、异步与并行编程

1. 异步 vs 并行

特性异步编程并行编程
目标非阻塞执行同时执行多个任务
线程使用通常不创建新线程使用多个线程
适用场景I/O密集型操作CPU密集型操作
关键字async/awaitParallel.For/ForEach

2. 混合使用示例

public async Task ProcessDataAsync(IEnumerable<Data> data)
{
    // 并行处理数据项
    var processedData = data.AsParallel()
                           .Select(d => ProcessItem(d))
                           .ToList();
    
    // 异步保存结果
    await SaveResultsAsync(processedData);
}

private Data ProcessItem(Data d)
{
    // CPU密集型处理
    return new Data { /* ... */ };
}

private async Task SaveResultsAsync(IEnumerable<Data> data)
{
    // 异步保存到数据库
    await _dbContext.BulkInsertAsync(data);
}

六、异步编程中的常见问题

1. 死锁问题

​错误示例​​:

public async Task DeadlockExample()
{
    await Task.Run(async () =>
    {
        // 在UI线程上调用Wait()会导致死锁
        await SomeAsyncOperation().ConfigureAwait(false); // 解决方案
    }).Wait(); // 阻塞调用
}

​解决方案​​:

  • 使用ConfigureAwait(false)(非UI上下文)
  • 避免在异步方法中调用.Result.Wait()
  • 使用await而不是Task.Run包装异步操作

2. 上下文保留问题

// 默认情况下,await会捕获当前上下文(如UI线程)
public async Task UpdateUIAsync()
{
    var data = await GetDataAsync();
    // 自动回到UI线程
    textBox.Text = data;
}

// 如果不需要回到原始上下文
public async Task ProcessInBackgroundAsync()
{
    var data = await GetDataAsync().ConfigureAwait(false);
    // 不会回到原始上下文
    ProcessData(data);
}

3. 性能优化

​避免过度异步化​​:

// 不必要的异步包装
public async Task UnnecessaryAsync()
{
    await Task.Run(() => 
    {
        // 同步操作
        Thread.Sleep(1000);
    });
}

​正确做法​​:

  • 只对真正的I/O操作使用异步
  • 对CPU密集型操作考虑并行处理
  • 避免在热路径上使用异步(如游戏循环)

七、高级异步模式

1. 异步流(IAsyncEnumerable)

// 生成异步流
public async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
    for (int i = 0; i < count; i++)
    {
        await Task.Delay(100); // 模拟异步操作
        yield return i;
    }
}

// 使用异步流
await foreach (var number in GenerateNumbersAsync(10))
{
    Console.WriteLine(number);
}

2. 异步锁

private readonly SemaphoreSlim _semaphore = new(1, 1);

public async Task ProtectedOperationAsync()
{
    await _semaphore.WaitAsync();
    try
    {
        // 受保护的代码
    }
    finally
    {
        _semaphore.Release();
    }
}

3. 异步工厂模式

public interface IAsyncFactory<T>
{
    Task<T> CreateAsync();
}

public class AsyncDataFactory : IAsyncFactory<Data>
{
    public async Task<Data> CreateAsync()
    {
        await Task.Delay(100); // 模拟异步初始化
        return new Data();
    }
}

八、异步测试

1. 单元测试异步方法

[Fact]
public async Task TestAsyncMethod()
{
    // Arrange
    var service = new MyService();
    
    // Act
    var result = await service.GetAsync();
    
    // Assert
    Assert.NotNull(result);
}

// 使用Moq测试异步方法
[Fact]
public async Task TestWithMock()
{
    var mock = new Mock<IRepository>();
    mock.Setup(r => r.GetDataAsync())
        .ReturnsAsync(new Data { Id = 1 });
        
    var service = new MyService(mock.Object);
    var result = await service.GetData();
    
    Assert.Equal(1, result.Id);
}

2. 测试异步代码中的异常

[Fact]
public async Task TestException()
{
    // Arrange
    var service = new FaultyService();
    
    // Act & Assert
    await Assert.ThrowsAsync<InvalidOperationException>(
        () => service.FaultyOperationAsync());
}

九、异步性能监控

1. 使用DiagnosticSource

// 在异步方法中添加诊断事件
private static readonly DiagnosticSource _diagnosticSource = 
    new DiagnosticListener("MyAsyncComponent");

public async Task ProcessAsync()
{
    if (_diagnosticSource.IsEnabled("StartProcess"))
    {
        _diagnosticSource.Write("StartProcess", new { Timestamp = DateTime.UtcNow });
    }
    
    await Task.Delay(100);
    
    if (_diagnosticSource.IsEnabled("EndProcess"))
    {
        _diagnosticSource.Write("EndProcess", new { Duration = 100 });
    }
}

2. 使用AsyncLocal跟踪上下文

private static readonly AsyncLocal<string> _context = new();

public async Task OperationWithContext()
{
    _context.Value = "OperationStarted";
    await Task.Delay(100);
    Console.WriteLine(_context.Value); // 仍然可以访问
}

十、异步编程的未来发展

1. C#中的新特性

  • ​C# 8.0​​:IAsyncEnumerable
  • ​C# 9.0​​:异步流改进
  • ​C# 10.0​​:更高效的异步方法生成

2. .NET中的改进

  • ​.NET Core 3.0+​​:更好的异步IO性能
  • ​.NET 5+​​:统一的异步API
  • ​.NET 6+​​:更智能的异步调度器

十一、最佳实践总结

  1. ​优先使用async/await​​而非ContinueWith或Task.Result
  2. ​在UI应用中​​,确保异步操作回到UI线程(使用ConfigureAwait(false)谨慎)
  3. ​避免混合同步和异步代码​​(如Wait()和Result)
  4. ​为长时间运行的操作​​使用CancellationToken
  5. ​考虑异步流​​处理连续数据(如日志、传感器数据)
  6. ​测试异步代码​​时使用AsyncTestMethods
  7. ​监控异步性能​​以识别瓶颈
  8. ​保持异步方法链​​避免混合同步/异步调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_shenbing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值