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()
{// Arrangevar service = new MyService();// Actvar result = await service.GetAsync();// AssertAssert.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()
{// Arrangevar service = new FaultyService();// Act & Assertawait 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. ​保持异步方法链​​避免混合同步/异步调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/903808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 之 【模拟实现 list(节点、迭代器、常见接口)】(将三个模板放在同一个命名空间就实现 list 啦)

1.前提准备 (1) list 的底层结构一般是带头双向循环链表 (1)为避免命名冲突&#xff0c;需要创建一个命名空间来存放模拟实现的 list (2)下面模拟实现list时&#xff0c;声明和定义不分离(具体原因后续讲解) 2.完整实现 2.1 链表节点 template<class T>//节点写成类模板…

解决Win10虚拟机“网络连接不上”,“Ethernet0 网络电缆被拔出”的问题

一、情景引入 今天用Win10虚拟机打开浏览器发现&#xff1a; 很奇怪&#xff0c;平常都没有这个问题。 二、检查网络状态 点击更改适配器选项&#xff0c;发现如下&#xff1a; 三、解决问题 打开任务管理器&#xff0c;点击服务&#xff0c;搜索栏搜索&#xff1a;VM …

2025蓝桥杯省赛网络安全组wp

文章目录 黑客密室逃脱ezEvtxflowzipEnigma星际xml解析器EBC-TrainAES-CBC 黑客密室逃脱 提示猜文件名&#xff0c;猜几个常见的&#xff0c;app.py读到源码 这里也是脑抽了一下&#xff0c;把密钥看成1236了。。。卡了五分钟左右&#xff0c;解出来的时候已经降到300多分了&a…

算法查找目录

1. 基础数据结构 数组与链表 动态数组 实现与自动扩容机制均摊分析ArrayList/Vector实现 单向链表 基本操作(插入、删除、查找)链表反转环检测(Floyd判圈算法) 双向链表 插入删除操作优化双向遍历优势边界情况处理 循环链表 约瑟夫环问题单向循环链表双向循环链表 跳表 基本原…

Windows11 管理员用户下无权限操作的解决方法

问题 Program Files 目录下无权限进行写入操作。 Windows 各种功能因权限不足无法访问。 启动某些应用程序时&#xff0c;可能会遇到 用户账户控制 (UAC, User Account Control) 弹窗提示&#xff0c;要求确认是否允许该程序对设备进行更改。 等等问题 解决方法 运行 p…

.NET中,const和readonly区别

在.NET中&#xff0c;const和readonly都用于定义不可变的值&#xff0c;但它们在行为和使用场景上有显著区别。以下是两者的详细对比&#xff1a; 初始化时机 • const ◦ 编译时常量&#xff0c;必须在声明时赋值。 ◦ 值在编译时确定&#xff0c;并被直接嵌入到IL代码中&…

邮件分类特征维度实验分析

活动发起人小虚竹 想对你说&#xff1a; 这是一个以写作博客为目的的创作活动&#xff0c;旨在鼓励大学生博主们挖掘自己的创作潜能&#xff0c;展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴&#xff0c;那么&#xff0c;快来参加吧&#xff01…

数字智慧方案6158丨智慧医疗解决方案精华版(58页PPT)(文末有下载方式)

数字智慧方案6158丨智慧医疗解决方案精华版 详细资料请看本解读文章的最后内容。 引言 随着信息技术的飞速发展&#xff0c;智慧医疗已成为现代医疗体系的重要组成部分。本文将对《数字智慧方案6158丨智慧医疗解决方案精华版》进行详细解读&#xff0c;探讨如何通过先进的技…

Fiori学习专题二十三: Filtering

这节课我们将针对list增加一个筛选功能。 1.首先改造下InvoiceList.view.xml&#xff0c;加入id方便找到它以及标签 <mvc:ViewcontrollerName"ui5.walkthrough.controller.InvoiceList"xmlns"sap.m"xmlns:core"sap.ui.core"xmlns:mvc"s…

大语言模型 05 运行、微调的显存计算详解与优化 全量微调、LoRA 优化策略

写在前面 随着Transformer架构的大语言模型&#xff08;LLM&#xff09;不断发展&#xff0c;其参数规模也在迅速增加。无论是进行模型推理还是微调训练&#xff0c;GPU显存消耗都是开发和应用LLM时的重要考量。本文将详细探讨大模型运行&#xff08;推理&#xff09;与微调时…

对Electron打包的exe文件进行反解析

一、了解 Electron 打包的 exe&#xff0c;本质上就是打包了网页 (HTMLCSSJS)&#xff0c;核心文件是 app.asar。超级容易还原&#xff0c;还原率接近 100% 为什么 Electron 特别容易&#xff1f; 因为 Electron 根本没有真正编译成机器码&#xff0c;它只是把网页资源&…

【Vue2】1-创建一个Vue实例

Vue2官方文档 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head&g…

【C语言练习】015. 声明和初始化指针

015. 声明和初始化指针 015. 声明和初始化指针1. 声明指针示例1:声明一个指向整数的指针2. 初始化指针示例2:将指针初始化为`NULL`示例3:将指针初始化为某个变量的地址示例4:将指针初始化为动态分配的内存地址3. 使用指针访问和修改变量的值示例5:使用指针访问和修改变量的…

好未来golang后端开发

OSI网络模型 TCP和UDP对比 HTTP和HTTPS对比 B树 HTTP常见状态码 线程和进程的区别 goroutine的调度模型GMP 常见的排序了解哪些 快速排序 func quickSort(data []int) {if len(data) < 1 {return}base : data[0]l, r : 0, len(data)-1for i : 1; i < r; {if data[i] &g…

(持续更新)Ubuntu搭建LNMP(Linux + Nginx + MySQL + PHP)环境

LNMP&#xff08;Linux Nginx MySQL PHP&#xff09;环境是在Linux操作系统上构建的一个高性能Web服务器环境。M也可以指代其他数据库&#xff0c;P也可以指代Python 1. 准备Linux系统 确保你已经在一台服务器或虚拟机上安装了Linux操作系统。推荐使用Ubuntu、CentOS或Debi…

服务器频繁重启日志分析与诊断

从你提供的日志来看&#xff0c;系统确实经历了多次重启。这个日志行显示的是&#xff1a; reboot system boot 6.8.0-58-generic Tue Apr 29 17:54 - 14:26 (20:31)这表示系统在4月29日17:54启动&#xff0c;运行了约20小时31分钟后&#xff0c;于次日14:26结束&#xff08;可…

如何提升个人的稳定性?

提升自我的稳定性是一个系统性工程&#xff0c;需要从内在认知、情绪管理、行为习惯到外在环境等多个维度进行优化。 以下是一些具体建议&#xff0c;帮助你逐步增强内心的稳定感&#xff1a; 一、内在认知调整 1. 建立清晰的自我认知 通过反思&#xff08;如写日记、冥想…

数值求解Eikonal方程的方法及开源实现

Eikonal方程是一类非线性偏微分方程&#xff0c;形式为 ( |\nabla u(x)| f(x) )&#xff0c;常见于波传播、几何光学、最短路径等问题。以下是数值求解Eikonal方程的方法及开源实现参考&#xff1a; 一、数值求解方法 有限差分法&#xff08;FDM&#xff09; 快速行进法&#…

基于Redis实现-用户签到

基于Redis实现-用户签到 这个功能将使用到Redis中的BitMap来实现。 我们按照月来统计用户签到信息&#xff0c;签到记录为1&#xff0c;未签到则记录为0 把每一个bit位对应当月的每一天&#xff0c;形成了映射关系。用0和1标示业务状态&#xff0c;这种思路称为位图(BitMap)。…

如何用GPU Instancing来优化树木草石重复模型

1&#xff09;如何用GPU Instancing来优化树木草石重复模型 2&#xff09;Unity ASTC压缩后的纹理在部分安卓机型上不显示 3&#xff09;现在大部分项目的竖版UI设计分辨率是多少 4&#xff09;Android上拖拽物体不实时跟随手指的问题 这是第430篇UWA技术知识分享的推送&#x…