.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以实现异步写入操作)。
//最常见用到的异步调用的,就是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; }
这段代码还可以这样写
//在同步方法中调用异步方法 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)可以避免不必要的上下文切换。