實現異步操作,除了用 async 和 await 這對組合之外,還可以通過委托對象的 BeginInvoke( ) 和 EndInvoke( ) 來完成 ,
為簡單起見,我們直接使用系統為我們提供的內置委托 Func<T1,T2,...TResult>(或者不帶返回值的Action<T1,T2...>委托也可以) ,代碼如下:
(注:目前.net5 core 還不支持 BeginInvoke() 和 EndInvoke() 這種異步調用形式,需要用 .net5 framework環境 。)
class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); Console.WriteLine("Main Thread ID = " + Thread.CurrentThread.ManagedThreadId); Demo51(); Console.ReadLine(); } private static int Demo51() { //定義一個Func<int,int>類型的委托 fx,功能:輸入一個整數,+100后輸出。 Func<int, int> fx = new Func<int, int>( x => { Console.WriteLine("Func<int, int>() Thread ID = " + Thread.CurrentThread.ManagedThreadId); return x + 100; } ); int ret = fx.Invoke(28); // Invoke() 是同步調用,即在當前主線程中調用 fx // BeginInvoke() 是異步調用,主線程會在線程池中取一個新線程來執行 fx,主線程會繼續往下執行 IAsyncResult iar = fx.BeginInvoke(28, null, null); // do something1....這里可以加入業務代碼 // EndInvoke() 用於獲取 fx 的執行結果並釋放線程使用的資源,在獲取到結果之前主線程不會繼續往下執行 int val = fx.EndInvoke(iar); // do something2....這里可以加入業務代碼 return val; } }
運行程序其結果如下,可以看到調用 fx.Invoke( ) 和 fx.BeginInvoke( )的運行線程是不一樣的。
在上面的例子中,當主線程執行到 "int val = fx.EndInvoke(iar);" 這條語句時如果 fx 還未執行完成,
主線程不會繼續往下執行,會一直等待 fx 的執行結果,這種方式叫 "等待直到完成(wait-until-done)" 模式 ,
除了這種模式外,還有另外2種模式,分別是 "輪詢(polling)"模式 和 "回調(callback)"模式,具體如下:
2. 輪詢(polling)模式
private int Demo52() {
//定義一個Func<int,int>類型的委托 fx,功能:輸入一個整數,+100后輸出。 Func<int, int> fx = new Func<int, int>( x => { Console.WriteLine("Func<int, int>() Thread ID = " + Thread.CurrentThread.ManagedThreadId); return x + 100; } );
// BeginInvoke() 是異步調用,即在線程池中取一個新線程來執行 fx IAsyncResult iar = fx.BeginInvoke(28, null, null); // do something1....這里可以加入業務代碼 // 判斷 IAsyncResult.IsCompleted 屬性來輪詢 fx 是否執行完成 while (iar.IsCompleted == false) { // do something....這里可以加入業務代碼 } // EndInvoke() 用於獲取 fx 的執行結果並釋放線程使用的資源 int val = fx.EndInvoke(iar); // do something2....這里可以加入業務代碼 return val; }
輪詢(polling)模式通過 IAsyncResult.IsCompleted 屬性來判斷 fx 是否執行完成 ,
我們可以在完成前和完成后做一些業務處理(測試略)。
3. 回調(callback)模式
private int callbackResult=0; private void CallbackFunc(IAsyncResult iar) { // state="test param",對應fx.BeginInvoke()的最后一個參數 string state = (string)iar.AsyncState; // 強制轉型用於后面獲取委托對象 fx AsyncResult ar = (AsyncResult)iar; Func<int, int> fx = (Func<int, int>)ar.AsyncDelegate; int callbackResult = fx.EndInvoke(iar); } private int Demo53() {
//定義一個Func<int,int>類型的委托 fx,功能:輸入一個整數,+100后輸出。 Func<int, int> fx = new Func<int, int>( x => { Console.WriteLine("Func<int, int>() Thread ID = " + Thread.CurrentThread.ManagedThreadId); return x + 100; } );
// BeginInvoke() 是異步調用,即在線程池中取一個新線程來執行 fx IAsyncResult iar = fx.BeginInvoke(28, CallbackFunc, "test param"); // do something....這里可以加入業務代碼 // 在回調方法執行完成前,此值是0。 return callbackResult; }
回調(callback)模式中, fx.BeginInvoke( )第二個參數對應的回調函數 CallbackFunc 的簽名必須符合
"void FuncName(IAsyncResult iar)" 的形式,最后一個參數是 object 類型,
可以傳入任意對象,在回調函數中用 iar.AsyncState 取值后做強制轉型就可以了(測試略)。
=================================分割線=================================
除了 async / await 和 delegate.BeginInvoke( ) / EndInvoke( ) 可以進行異步操作之外,
.net 還為我們提供了 System.Threading.Timer 、System.Threading.Tasks.Parallel 、
System.ComponentModel.BackgroundWorker 等類來做並行處理,
它們中部分更適用於GUI環境(如winform、WPF等),后續根據需要再做介紹。