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     }

 

 

 

 

 

*** |  以上內容僅為學習參考、學習筆記使用  | *** 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM