在異步程序中,程序代碼不需要嚴格按照編寫時的順序執行
為了改善代碼性能,有時候需要在一個新的線程中運行一部分代碼
有時候無需創建新的線程,但為了更好的利用單個線程的能力,需要改變代碼的執行順序
也就是說:
異步編程賦予代碼非順序執行的能力,讓程序能夠在部分耗時操作的同時,干其他的事情
一、通過委托實現異步
如果委托對象在調用列表中只有一個方法(引用方法),它就可以異步執行這個方法
委托類有 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 }
*** | 以上內容僅為學習參考、學習筆記使用 | ***