异步调用 (转载)
異步操作通常用于執行完成時間可能較長的任務,如打開大文件、連接遠程計算機或查詢數據庫。異步操作在主應用程序線程以外的線程中執行。應用程序調用方法異步執行某個操作時,應用程序可在異步方法執行其任務時繼續執行。
.NET框架能夠對任何方法進行異步調用。進行異步調用時,需要定義與異步調用的方法具有相同簽名的委托。公共語言運行時會自動使用適當的簽名為該委托定義BeginInvoke和EndInvoke方法。
BeginInvoke方法用于啟動異步調用。它與需要異步執行的方法具有相同的參數。此外,它還有兩個可選參數。第一個參數是一個AsyncCallback委托,該委托引用在異步調用完成時要調用的方法。第二個參數是一個用戶定義的對象,該對象可向回調方法傳遞數據。BeginInvoke立即返回,不會等待異步調用完成,被調用的方法將在線程池線程中執行。因此,提交請求的原始線程與執行異步方法的線程池線程是并行執行的。BeginInvoke會返回一個IAsyncResult對象,可以使用該對象來監視異步調用進度,也可將該對象傳遞給EndInvoke方法,以獲取異步執行的方法的返回值。
EndInvoke方法用于檢索異步調用的結果。調用BeginInvoke方法后可隨時調用EndInvoke方法;如果異步調用尚未完成,EndInvoke方法將一直阻塞調用線程,直到異步調用完成后才允許調用線程執行。EndInvoke方法的參數包括需要異步執行的方法的out和ref參數,以及由BeginInvoke返回的IAsyncResult對象。因此,通過EndInvoke方法可以獲得異步調用的方法的所有輸出數據,包括返回值、out和ref參數。
AsyncCallback委托表示在異步操作完成時調用的回調方法,其定義如下:
public delegate void AsyncCallback(IAsyncResult ar);
System.IAsyncResult接口表示異步操作的狀態。IAsyncResult接口由包含可異步操作的方法的類實現。它是啟動異步操作的方法的返回類型,也是結束異步操作的方法的第三個參數的類型。當異步操作完成時,IAsyncResult對象也將傳遞給由AsyncCallback委托調用的方法。支持IAsyncResult接口的對象存儲異步操作的狀態信息,并提供同步對象以允許線程在操作完成時終止。IAsyncResult接口定義了四個公開屬性,通過它們可以獲取異步調用的狀態。
—? AsyncState:獲取用戶定義的對象,它限定或包含關于異步操作的信息。
—? AsyncWaitHandle:獲取用于等待異步操作完成的 WaitHandle。
—? CompletedSynchronously:獲取異步操作是否同步完成的指示。
—? IsCompleted:獲取異步操作是否已完成的指示。
使用BeginInvoke和EndInvoke進行異步調用的常用方法主要有四種:
— 調用BeginInvoke方法啟動異步方法,進行某些操作,然后調用EndInvoke方法來一直阻止請求線程到調用完成。
— 調用BeginInvoke方法啟動異步方法,使用System.IAsyncResult.AsyncWaitHandle屬性獲取WaitHandle,使用它的WaitOne方法一直阻止執行直到發出WaitHandle信號,然后調用EndInvoke方法。
— 調用BeginInvoke方法啟動異步方法,輪詢由BeginInvoke返回的IAsyncResult,確定異步調用何時完成,然后調用EndInvoke。
— 調用BeginInvoke方法啟動異步方法時,將代表異步方法完成時需要回調的方法的委托傳遞給BeginInvoke。異步調用完成后,將在ThreadPool線程上執行該回調方法。在該回調方法中調用EndInvoke。
每種方法都是通過BeginInvoke方法來啟動異步方法,調用EndInvoke方法來完成異步調用。
1.直接調用EndInvoke 方法等待異步調用結束
異步執行方法的最簡單的方式是通過調用委托的BeginInvoke方法來開始執行方法,在主線程上執行一些工作,然后調用委托的EndInvoke方法。EndInvoke可能會阻止調用線程,因為它直到異步調用完成之后才返回。這種技術非常適合于文件或網絡操作,但是由于EndInvoke會阻止它,所以不要從服務于用戶界面的線程中調用它。
下面的代碼說明了如何使用這種方法來進行異步調用,并獲得異步方法的結果:
// AsynCall1.cs
// 異步調用示例: 直接調用EndInvoke 方法等待異步調用結束
using System;
using System.Threading;
// 定義異步調用方法的委托
// 它的簽名必須與要異步調用的方法一致
public delegate int AsynComputeCaller(ulong l, out ulong factorial);
public class Factorial
{
??? // 計算階乘
??? public ulong Compute(ulong l)
??? {
??????? // 不要太快 :-)
??????? Thread.Sleep(50);
??????? if (l == 1)
??????? {
??????????? return 1;
??????? }
??????? else
??????? {
??????????? return l * Compute(l - 1);
??????? }
??? }
??? // 要異步調用的方法
??? // 1. 調用Factorial方法來計算階乘,并用out參數返回
??? // 2. 統計計算階乘所用的時間,并返回該值
??? public int AsynCompute(ulong l, out ulong factorial)
??? {
??????? Console.WriteLine("開始異步方法");
???????
??????? DateTime startTime = DateTime.Now;
??????? factorial = Compute(l);
??????? TimeSpan usedTime =
????????????????????? new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
???????
??????? Console.WriteLine("結束異步方法");
??????? return usedTime.Milliseconds;
??? }
}
public class Test
{
??? public static void Main()
??? {
??????? // 創建包含異步方法的類的實例
??????? Factorial fact = new Factorial();
??????? // 創建異步委托
??????? AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);
??????? Console.WriteLine("啟動異步調用");
??????? ulong l = 30;
??????? ulong lf;
??????? // 啟動異步調用
??????? IAsyncResult result = caller.BeginInvoke(l, out lf, null, null);
??????? // 主線程進行一些操作
??????? Thread.Sleep(0);
??????? Console.WriteLine("主線程進行一些操作");
??????? // 調用EndInvoke來等待異步調用結束,并獲得結果
??????? int returnValue = caller.EndInvoke(out lf, result);
??????? // 異步調用的方法已經結束,顯示結果
??????? Console.WriteLine("已經得到結果:{0}的階乘為{1},計算時間為{2}毫秒",
?????????????????????????????? l, lf, returnValue);
??? }
}
2.使用 WaitHandle 等待異步調用結束
可以使用BeginInvoke方法返回的IAsyncResult的AsyncWaitHandle屬性來獲取WaitHandle。異步調用完成時會發出WaitHandle信號,可以通過調用WaitOne方法來等待它。
如果使用WaitHandle,則在異步調用完成之前或之后,在通過調用EndInvoke檢索結果之前,還可以執行其他處理。
下面的代碼說明了如何使用這種方法來進行異步調用,并獲得異步方法的結果:
// AsynCall2.cs
// 異步調用示例:使用 WaitHandle 等待異步調用結束
using System;
using System.Threading;
// 定義異步調用方法的委托
// 它的簽名必須與要異步調用的方法一致
public delegate int AsynComputeCaller(ulong l, out ulong factorial);
public class Factorial
{
??? // 計算階乘
??? public ulong Compute(ulong l)
??? {
??????? // 不要太快 :-)
??????? Thread.Sleep(50);
??????? if (l == 1)
??????? {
??????????? return 1;
??????? }
??????? else
??????? {
??????????? return l * Compute(l - 1);
??????? }
??? }
??? // 要異步調用的方法
??? // 1. 調用Factorial方法來計算階乘,并用out參數返回
??? // 2. 統計計算階乘所用的時間,并返回該值
??? public int AsynCompute(ulong l, out ulong factorial)
??? {
??????? Console.WriteLine("開始異步方法");
???????
??????? DateTime startTime = DateTime.Now;
??????? factorial = Compute(l);
??????? TimeSpan usedTime =
??????????????????????? new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
???????
??????? Console.WriteLine("結束異步方法");
??????? return usedTime.Milliseconds;
??? }
}
public class Test
{
??? public static void Main()
??? {
??????? // 創建包含異步方法的類的實例
??????? Factorial fact = new Factorial();
??????? // 創建異步委托
??????? AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);
??????? Console.WriteLine("啟動異步調用");
??????? ulong l = 30;
??????? ulong lf;
??????? // 啟動異步調用
??????? IAsyncResult result = caller.BeginInvoke(l, out lf, null, null);
??????? // 主線程進行一些操作
??????? Thread.Sleep(0);
??????? Console.WriteLine("主線程進行一些操作");
??????? // 等待WaitHandle接收到信號
??????? Console.WriteLine("等待WaitHandle接收到信號");
??????? result.AsyncWaitHandle.WaitOne();
??????? // 主線程進行一些操作
??????? Thread.Sleep(0);
??????? Console.WriteLine("異步方法已經結束,主線程進行另外一些操作");
??????? // 調用EndInvoke來獲得結果
??????? int returnValue = caller.EndInvoke(out lf, result);
??????? // 異步調用的方法已經結束,顯示結果
??????? Console.WriteLine("{0}的階乘為{1},計算時間為{2}毫秒",
????????????????????????????? l, lf, returnValue);
??? }
}
3.輪詢異步調用是否完成
可以使用由BeginInvoke方法返回的IAsyncResult的IsCompleted屬性來發現異步調用何時完成。從用戶界面的服務線程中進行異步調用時可以執行此操作。輪詢完成允許調用線程在異步調用在線程池線程上執行時繼續執行。
下面的代碼說明了如何使用這種方法來進行異步調用,并獲得異步方法的結果:
// AsynCall3.cs
// 異步調用示例: 輪詢異步調用是否完成
using System;
using System.Threading;
// 定義異步調用方法的委托
// 它的簽名必須與要異步調用的方法一致
public delegate int AsynComputeCaller(ulong l, out ulong factorial);
public class Factorial
{
??? // 計算階乘
??? public ulong Compute(ulong l)
??? {
??????? // 不要太快 :-)
??????? Thread.Sleep(50);
??????? if (l == 1)
??????? {
??????????? return 1;
??????? }
??????? else
??????? {
??????????? return l * Compute(l - 1);
??????? }
??? }
??? // 要異步調用的方法
??? // 1. 調用Factorial方法來計算階乘,并用out參數返回
??? // 2. 統計計算階乘所用的時間,并返回該值
??? public int AsynCompute(ulong l, out ulong factorial)
??? {
??????? Console.WriteLine("開始異步方法");
???????
??????? DateTime startTime = DateTime.Now;
??????? factorial = Compute(l);
??????? TimeSpan usedTime =
?????????????????????????????? new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
???????
??????? Console.WriteLine("\n結束異步方法");
??????? return usedTime.Milliseconds;
??? }
}
public class Test
{
??? public static void Main()
??? {
??????? // 創建包含異步方法的類的實例
??????? Factorial fact = new Factorial();
??????? // 創建異步委托
??????? AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);
??????? Console.WriteLine("啟動異步調用");
??????? ulong l = 30;
?????? ?ulong lf;
??????? // 啟動異步調用
??????? IAsyncResult result = caller.BeginInvoke(l, out lf, null, null);
??????? // 輪詢異步方法是否結束
??????? Console.WriteLine("主線程進行一些操作");
??????? while (result.IsCompleted == false)
??????? {
??????????? // 主線程進行一些操作
?????????? ?Thread.Sleep(10);
??????????? Console.Write(".");
??????? }
??????? // 主線程進行一些操作
??????? Thread.Sleep(0);
??????? Console.WriteLine("異步方法已經結束,主線程進行另外一些操作");
??????? // 調用EndInvoke來獲得結果
??????? int returnValue = caller.EndInvoke(out lf, result);
?????? ?// 異步調用的方法已經結束,顯示結果
??????? Console.WriteLine("已經得到結果:{0}的階乘為{1},計算時間為{2}毫秒",
?????????????????????????????? l, lf, returnValue);
??? }
}
4.在異步調用完成時執行回調方法
如果啟動異步調用的線程是不需要處理結果的線程,則可以在調用完成時執行回調方法。回調方法在線程池線程上執行。
若要使用回調方法,必須將引用回調方法的AsyncCallback委托傳遞給BeginInvoke。也可以傳遞包含回調方法將要使用的信息的對象。例如,可以傳遞啟動調用時曾使用的委托,以便回調方法能夠調用EndInvoke方法。
下面的代碼說明了如何使用這種方法來進行異步調用,并獲得異步方法的結果:
// AsynCall4.cs
// 異步調用示例: 在異步調用完成時執行回調方法
using System;
using System.Threading;
// 定義異步調用方法的委托
// 它的簽名必須與要異步調用的方法一致
public delegate int AsynComputeCaller(ulong l, out ulong factorial);
public class Factorial
{
??? // 計算階乘
??? public ulong Compute(ulong l)
??? {
??????? // 不要太快 :-)
??????? Thread.Sleep(50);
??????? if (l == 1)
??????? {
??????????? return 1;
??????? }
??????? else
??????? {
??????????? return l * Compute(l - 1);
??????? }
??? }
??? // 要異步調用的方法
??? // 1. 調用Factorial方法來計算階乘,并用out參數返回
??? // 2. 統計計算階乘所用的時間,并返回該值
??? public int AsynCompute(ulong l, out ulong factorial)
??? {
??????? Console.WriteLine("開始異步方法");
???????
??????? DateTime startTime = DateTime.Now;
??????? factorial = Compute(l);
??????? TimeSpan usedTime =
?????????????????????????????? new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
???????
??????? Console.WriteLine("結束異步方法");
??????? return usedTime.Milliseconds;
??? }
}
public class Test
{
??? static ulong l = 30;
??? static ulong lf;
??? public static void Main()
??? {
??????? // 創建包含異步方法的類的實例
??????? Factorial fact = new Factorial();
??????? // 創建異步委托
??????? AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);
??????? // 啟動異步調用
??????? Console.WriteLine("啟動異步調用");
??????? IAsyncResult result = caller.BeginInvoke(l, out lf,
??????????????????????????????? new AsyncCallback(CallbackMethod), caller);
??????? // 主線程進行一些操作
??????? Thread.Sleep(0);
? ??????Console.WriteLine("主線程進行一些操作");
??????? Console.WriteLine("按Enter鍵結束程序...");
??????? Console.ReadLine();
??? }
??? // 在異步調用完成時執行的回調方法
??? // 該回調方法的簽名必須與AsyncCallback委托一致
??? static void CallbackMethod(IAsyncResult ar)
??? {
??????? // 獲取委托
??????? AsynComputeCaller caller = (AsynComputeCaller)ar.AsyncState;
??????? // 調用EndInvoke來獲得結果
??????? int returnValue = caller.EndInvoke(out lf, ar);
??????? // 異步調用的方法已經結束,顯示結果
??????? Console.WriteLine("已經得到結果:{0}的階乘為{1},計算時間為{2}毫秒",
????????????????????? ?????????l, lf, returnValue);
??? }
}
轉載于:https://www.cnblogs.com/vincedotnet/archive/2007/09/04/881045.html
總結