在异步程序中,程序代码不需要严格按照编写时的顺序执行
为了改善代码性能,有时候需要在一个新的线程中运行一部分代码
有时候无需创建新的线程,但为了更好的利用单个线程的能力,需要改变代码的执行顺序
也就是说:
异步编程赋予代码非顺序执行的能力,让程序能够在部分耗时操作的同时,干其他的事情
一、通过委托实现异步
如果委托对象在调用列表中只有一个方法(引用方法),它就可以异步执行这个方法
委托类有 BeginInvoke,EndInvoke 方法,可以用以下方式使用:
- 当调用 BeginInvoke 方法时,它开始在一个独立线程上执行引用方法,并立即返回到原始线程;原始线程可以继续运行,而引用方法会在线程池大的线程中并行执行
- 当程序希望获取已完成的异步方法的结果时,可以检查 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性,或者调用 EndInvoke 方法等待委托的完成
使用这一过程有三种标准模式,区别在于:原始线程如何知道发起的线程已经完成
- 一直等待到完成模式(wait until done):
原始线程发起异步方法并做了一些其他处理后,原始线程中断并等待异步方法完成后再继续
- 轮询模式(polling):
原始线程定期检查发起的异步方法线程是否完成,如果没有则继续做其他事情
- 回调模式(callback):
原始线程一直执行,无需等待,当发起的线程中引用方法完成后,发起的线程就调用回调方法,调用 EndInvoke 之前处理异步方法的结果
![]()
1 static void Main(string[] args)
2 {
3 Console.WriteLine("===== 同步调用 =====");
4 AddDel del = new AddDel(Add);
5 int result = del.Invoke(11, 89);
6 Console.WriteLine("计算结果:" + result);
7 Console.WriteLine("继续削铅笔...\n");
8 Console.ReadKey();
9
10 Console.WriteLine("===== 异步调用 =====");
11 IAsyncResult result_1 = del.BeginInvoke(22, 78, null, null);
12 Console.WriteLine("继续削铅笔...");
13 Console.WriteLine("计算结果:" + del.EndInvoke(result_1));
14 Console.ReadKey();
15
16 Console.WriteLine("\n===== 异步回调 =====");
17 del.BeginInvoke(33, 67, new AsyncCallback(AddAsync), "AsyncState:OK");
18 Console.WriteLine("继续削铅笔...");
19 Console.ReadKey();
20 }
21
22 // 委托
23 public delegate int AddDel(int a, int b);
24 // 加法计算
25 static int Add(int a, int b)
26 {
27 Console.WriteLine("开始计算:" + a + "+" + b);
28 // 模拟运行时间
29 Thread.Sleep(2000);
30 Console.WriteLine("计算完成!");
31 return a + b;
32 }
33 // 回调函数
34 static void AddAsync(IAsyncResult ar)
35 {
36 AddDel del = ((AsyncResult)ar).AsyncDelegate as AddDel;
37 Console.WriteLine("计算结果:" + del.EndInvoke(ar));
38 Console.WriteLine(ar.AsyncState);
39 }
![]()

二、通过 Task 实现异步
Task 类通常是以异步方式执行的单个操作,更适合在后台完成的一些小任务
由于 Task 对象执行的工作通常在线程池的线程上异步执行,而不是在程序主线程上同步执行
因此可以使用 Status 属性,还可以使用 IsCancele、IsCompleted 和 IsFaulted 属性来确认任务的状态
大多数情况下,lambda 表达式用于指定的任务是执行的工作
🙌🌰:
![]()
1 static void Main(string[] args)
2 {
3 Console.WriteLine("主线程正在执行业务处理!");
4 // 创建任务
5 Task task = new Task(() =>
6 {
7 Console.WriteLine("使用Task执行异步操作:");
8 for (int i = 0; i <= 10; i++)
9 {
10 Console.WriteLine("操作执行:" + i * 10 + "%");
11 Thread.Sleep(100);
12 }
13 Console.WriteLine("Task异步操作执行完成!");
14 });
15 // 启动任务,并安排到当前任务队列线程中执行任务
16 task.Start();
17 Thread.Sleep(500);
18 Console.WriteLine("主线程正在执行其他业务!");
19 Console.ReadKey();
20 }
![]()

三、通过 await/async 实现异步
C# 中的 Async 和 Await 关键字是异步编程的核心
通过这两个关键字,可以使用 .NET Framework、.NET Core 或 Windows 运行时中的资源,轻松创建异步方法(几乎与创建同步方法一样轻松)
而使用 Async 关键字定义的异步方法简称为 "异步方法"
🙌🌰:
![]()
1 static void Main(string[] args)
2 {
3 Console.WriteLine("主线程正在执行第一项业务!");
4 AsyncTest();
5 Thread.Sleep(300);
6 Console.WriteLine("主线程正在执行第二项业务!");
7 Thread.Sleep(300);
8 Console.WriteLine("主线程正在执行第三项业务!");
9 Thread.Sleep(500);
10 Console.WriteLine("主线程正在执行最后一项业务!");
11 Console.ReadKey();
12 }
13
14 public static async void AsyncTest()
15 {
16 Console.WriteLine("开始执行异步操作:");
17 // await 不会开启新的线程
18 // 需要自己创建Task,才会真正的去创建线程
19 //await new Program().AsyncTaskFun();
20 await Task.Run(() =>
21 {
22 new Program().AsyncTaskFun();
23 });
24 Console.WriteLine("异步操作执行完成!");
25 }
26
27 public async Task<int> AsyncTaskFun()
28 {
29 Console.WriteLine("异步操作正在执行:");
30 for (int i = 1; i <= 10; i++)
31 {
32 Console.WriteLine("操作执行:" + i * 10 + "%");
33 Thread.Sleep(100);
34 }
35 return 2;
36 }
![]()

四、通过 BackgroundWorker 实现异步
有时候我们需要在后台新建一个线程默默完成一项工作,过程中时不时同主线程进行通信,这就是 BackgroundWorker 的主要任务
BackgroundWorker 类允许在单独的线程上执行某个可能导致用户界面(UI)停止响应的耗时操作,并且想要一个响应式的UI来反应当前耗时操作的进度
🙌🌰:
![]()
1 public partial class Form1 : Form
2 {
3 // BackgroundWorker实例
4 BackgroundWorker bw = new BackgroundWorker();
5
6 public Form1()
7 {
8 InitializeComponent();
9 bw.WorkerReportsProgress = true;
10 bw.WorkerSupportsCancellation = true;
11 bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
12 bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
13 bw.DoWork += new DoWorkEventHandler(bw_DoWork);
14 progressBar1.Maximum = 100;
15 }
16 // 进度条变化
17 private void bw_ProgressChanged(Object sender, ProgressChangedEventArgs e)
18 {
19 progressBar1.Value = e.ProgressPercentage;
20 label1.Text = e.UserState.ToString();
21 label1.Update();
22 }
23 // 后台任务完成
24 private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
25 {
26 MessageBox.Show("后台操作执行完成!");
27 }
28 // 后台具体任务
29 private void bw_DoWork(object sender, DoWorkEventArgs e)
30 {
31 BackgroundWorker bw = sender as BackgroundWorker;
32 int num = 101;
33 for (int i = 0; i < num; i++)
34 {
35 if (bw.CancellationPending)
36 {
37 bw.ReportProgress(i, $"当前进度{i}%, 已停止!");
38 return;
39 }
40 bw.ReportProgress(i, $"当前进度{i}%");
41 Thread.Sleep(100);
42 }
43 return;
44 }
45 // 开始按钮
46 private void button1_Click(object sender, EventArgs e)
47 {
48 if (bw.IsBusy) return;
49 bw.RunWorkerAsync();
50 }
51 // 停止按钮
52 private void button2_Click(object sender, EventArgs e)
53 {
54 bw.CancelAsync();
55 }
56 }
![]()
