作者: littleboy 2023-10-14 16:09:22

C#-异步

在 C# 中,async/awaitTask 提供了现代化的异步编程模型(TAP),让代码既保持同步的可读性,又能避免阻塞线程,从而提升应用的响应性与性能。

通过合理使用 C# 异步特性,可以让应用在保持代码清晰的同时,显著提升性能与用户体验。

核心原理

是:await 会在等待的任务未完成时让出当前线程,任务完成后恢复执行等待点之后的代码。编译器会生成状态机保存上下文,确保恢复时环境一致。

基本用法示例

下面的示例展示了如何并行启动多个任务,并在全部完成后更新 UI 或输出结果:

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
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("开始准备早餐...");
var eggsTask = FryEggsAsync(2);
var hashBrownTask = FryHashBrownsAsync(3);
var toastTask = MakeToastWithButterAndJamAsync(2);
await Task.WhenAll(eggsTask, hashBrownTask, toastTask);
Console.WriteLine("早餐准备完成!");
}
static async Task<string> FryEggsAsync(int count)
{
Console.WriteLine($"煎 {count} 个鸡蛋...");
await Task.Delay(3000);
return "鸡蛋完成";
}
static async Task<string> FryHashBrownsAsync(int count)
{
Console.WriteLine($"煎 {count} 个薯饼...");
await Task.Delay(3000);
return "薯饼完成";
}
static async Task<string> MakeToastWithButterAndJamAsync(int slices)
{
Console.WriteLine($"烤 {slices} 片面包...");
await Task.Delay(2000);
Console.WriteLine("抹黄油和果酱");
return "吐司完成";
}
}

要点解析

避免阻塞:不要用 .Wait().Result,应使用 await 保持线程可用。

并行执行:先启动任务,最后统一 await Task.WhenAll,减少总耗时。

任务组合:异步方法中可包含同步步骤,整体仍是异步操作。

异常处理await 会在任务失败时抛出异常,可用 try/catch 捕获;多个异常会封装在 AggregateException 中。

UI 场景:在 WinForms/WPF 中,await 默认会回到原同步上下文,可直接更新控件;使用 Task.Run 时需 Invoke 回 UI 线程。

最佳实践

优先 async/await,减少显式线程管理。

I/O 密集型 用异步 API(如 HttpClient.GetAsync),

CPU 密集型Task.Run 封装。

组合任务Task.WhenAllTask.WhenAny 提高效率。避免 async void,除非是事件处理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static async Task<object[]> FryEggsAsync(int count)
{
IAssemblyDoc assemblyDoc = (IAssemblyDoc)model;

Console.WriteLine($" {count} 还原...");
//await Task.Run(() => assemblyDoc.ResolveAllLightweight());
await Task.Delay(13000);
Console.WriteLine($" {count} 还原完成...");
object[] Components = assemblyDoc.GetComponents(false);
return Components;
}
private async void button9_Click(object sender, EventArgs e)
{
var eggsTask = FryEggsAsync(2);

string 施工单号 = (string)this.Invoke((MethodInvoker)delegate
{
Microsoft.VisualBasic.Interaction.InputBox("施工单号", "", "施工单号");
});
object[] Components = await eggsTask;//等待任务完成才执行这个
}

参考

C# 异步编程精要