C# 异步编程的几种方式

在异步程序中,程序代码不需要严格按照编写时的顺序执行

为了改善代码性能,有时候需要在一个新的线程中运行一部分代码

有时候无需创建新的线程,但为了更好的利用单个线程的能力,需要改变代码的执行顺序

也就是说:

异步编程赋予代码非顺序执行的能力,让程序能够在部分耗时操作的同时,干其他的事情

一、通过委托实现异步

如果委托对象在调用列表中只有一个方法(引用方法),它就可以异步执行这个方法

委托类有 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     }

 

 

 

 

 

*** |  以上内容仅为学习参考、学习笔记使用  | *** 

posted @ 2021-10-09 20:48  Mr.Cat~  阅读(4423)  评论(0编辑  收藏  举报