C#知识整理-异步编程
.NET 提供了执行异步操作的三种模式:
- 基于任务的异步模式 (TAP),该模式使用单一方法表示异步操作的开始和完成。TAP 是在 .NET Framework 4 中引入的。这是在 .NET 中进行异步编程的推荐方法。C# 中的async和await关键词以及 Visual Basic 中的Async和Await运算符为 TAP添加了语言支持。
- 基于事件的异步模式 (EAP),是提供异步行为的基于事件的旧模型。 这种模式需要后缀为Async的方法,以及一个或多个事件、事件处理程序委托类型和EventArg派生类型。 EAP 是在 .NET Framework 2.0 中引入的。 建议新开发中不再使用这种模式。
- 异步编程模型 (APM) 模式(也称为IAsyncResult模式),这是使用IAsyncResult接口提供异步行为的旧模型。 在这种模式下,同步操作需要Begin和End方法(例如,BeginWrite和EndWrite以实现异步写入操作)。
这里只讨论TAP模式
Task/async/await
关键字:
async:接口无法使用async,配合await使用将方法包装为状态机
await:等待一个异步方法完成,可能会来到一个新的线程上
Task:异步任务
async Task/Task: 定义异步方法
async void:无返回值的异步方法,一般async void只有在定义wpf event方法会使用到,async void有一个问题就是无法聚合异常,在调用async void的方法时异常无法捕获可以想见会引发很多问题,所以一般被调用的方法使用async Task
这次的代码使用WPF做案例
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 | //最常见用到的异步调用的,就是Web API的调用 //先定义一个WebAPI [Route( "api/[controller]" )] [ApiController] public class DemoController : ControllerBase { [HttpGet] public string Get() { return "Test Get API" ; } } //在WPF里 //添加3个组件 <Grid> <Button Content= "GetApiContent" HorizontalAlignment= "Center" Margin= "0,81,0,0" VerticalAlignment= "Top" Click= "Button_Click" /> <TextBlock Name= "text_block" HorizontalAlignment= "Center" TextWrapping= "Wrap" Text= "TextBlock" VerticalAlignment= "Center" Height= "192" Width= "540" /> <TextBox HorizontalAlignment= "Left" Margin= "143,83,0,0" TextWrapping= "Wrap" Text= "TextBox" VerticalAlignment= "Top" Width= "120" /> </Grid> //对应的cs文件 private async void Button_Click( object sender, RoutedEventArgs e) { var url = "https://localhost:7163/api/Demo" ; HttpClient client = new HttpClient(); var result = await client.GetAsync(url).ConfigureAwait( true ); string content = string .Empty; if (result.IsSuccessStatusCode) { content = await result.Content.ReadAsStringAsync(); } text_block.Text += content; var getHeavyJobResult = await HeavyJobAsync(); text_block.Text += getHeavyJobResult; } private async Task< int > HeavyJobAsync() { await Task.Delay(2000).ConfigureAwait( false ); //.ConfigureAwait(true);; return 100; } |
这段代码还可以这样写
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 | //在同步方法中调用异步方法 private void Button_Click2( object sender, RoutedEventArgs e) { var url = "https://localhost:7163/api/Demo" ; HttpClient client = new HttpClient(); var result = client.GetAsync(url).GetAwaiter().GetResult(); string content = string .Empty; if (result.IsSuccessStatusCode) { content = result.Content.ReadAsStringAsync().GetAwaiter().GetResult(); } text_block.Text += content; //getAwaiter和await一样会释放当前线程 var getHeavyJobResult = HeavyJobAsync().GetAwaiter().GetResult(); text_block.Text += getHeavyJobResult; //不建议使用.Result来写,这里会阻塞线程 var getHeavyJobResult2 = HeavyJobAsync().Result; text_block.Text += getHeavyJobResult2; } private async Task< int > HeavyJobAsync() { //大家可以尝试看看ConfigureAwait(false)和ConfigureAwait(true) 有什么不同 await Task.Delay(2000).ConfigureAwait( false ); //.ConfigureAwait(true); return 100; } |
ConfigureAwait在WPF中可以避免UI线程上的死锁
- 如果异步方法在UI线程上启动,并且没有使用ConfigureAwait(false),那么当await操作完成后,它会继续在UI线程上执行,这可能导致死锁,因为UI线程可能正在等待用户输入或其他操作。
- 使用ConfigureAwait(false)可以避免不必要的上下文切换。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Blazor Hybrid适配到HarmonyOS系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库