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)可以避免不必要的上下文切换。
 
posted @   Terry841119  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Blazor Hybrid适配到HarmonyOS系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
点击右上角即可分享
微信分享提示