異步編程
- 同步編程,請求響應模型,同步化、順序化、事務化。
- 異步編程,事件驅動模型,以 Fire and Forget 方式實現。
異步編程模式
-§- 異步編程模型 (APM) 模式: IAsyncResult 模式,異步操作需要 Begin 和 End 方法;
-§- 基於事件的異步模式(EAP):事件、事件處理程序委托類型和 EventArg 派生類型;
-§- 基於任務的異步模式(TAP):推薦模式,.NET Framework 4 引入,基於 System.Threading.Tasks 命名空間,利用一種方法表示異步操作的啟動和完成;
類 LogicalMethodInfo 提供 Invoke 和 BeginInvoke 方法支持同步或異步執行委托的方法:
public sealed class System.Web.Services.Protocols.LogicalMethodInfo{
public object[] Invoke(object target, object[] values);
public IAsyncResult BeginInvoke(object target, object[] values, AsyncCallback callback, object asyncState);
public object[] EndInvoke(object target, IAsyncResult asyncResult);
}
其中,target 是委托方法所屬的類實例,values 是委托方法的參數列表。
異步方法調用模式
三種方法 調用模式區別:原線程獲取異步線程已經完成的消息的方式。

其中,BeginInvoke 方法用於啟動異步調用,EndInvoke 方法用於檢索並獲取異步調用結果然后釋放線程占用的資源。BeginInvoke 立即返回,不等待異步方法調用完成,同時在調用時創建一個 AsyncResult 類對象,但是 BeginInvoke 返回 IAsyncResult 接口的引用,可用於監視異步方法的調用進度,調用 BeginInvoke 后可隨時調用 EndInvoke 方法。
其中,IAsyncResult 接口 的 AsyncState 屬性獲取 Object 類型的對象可以作為 BeginInvoke 方法調用時的 State 參數(可根據需要自定義)。 委托定義和異步委托方法定義為:
public delegate int MyDel(string str);
public static int DelFun(string str){
Console.WriteLine("異步線程執行方法:DelFun() in " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(3000);
return str.Length;
}
注:BeginInvoke 的參數是在編譯時根據委托的定義動態生成的,前面參數的個數和類型與委托定義中的參數個數和類型相同,后兩個參數分別是 AsyncCallback 和 Object 類型。BeginInvoke 的調用者的方法列表有且只能有一個方法。BeginInvoke 第三個參數可為任意類型,最終都可通過 AsyncState 屬性獲得對應值。
等待模式 Waiting-Until-Done
如果異步方法調用未完成,EndInvoke 將一直阻塞到異步方法調用完成才執行。其中,等待 WaitHandle 是一項常用的線程同步技術,可以通過 BeginInvoke 返回的 IAsyncResult 的 AsyncWaitHandle 屬性調用 WaitOne() 方法阻止當前線程,直到獲取到異步方法調用完成時發出的 WaitHandle 信號。
public static void AsyncWaitingTest()
{
MyDel myDel = new MyDel(TestClass.DelFun);
Console.WriteLine("主線程:AsyncWaitingTest() in " + Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = myDel.BeginInvoke("sqh", null, null);
Console.WriteLine("主線程繼續執行... in " + Thread.CurrentThread.ManagedThreadId);
while (!iar.AsyncWaitHandle.WaitOne(1000)){
Console.WriteLine("Waiting... AsyncMethod()");
}
int strLen = myDel.EndInvoke(iar);
Console.WriteLine("Str Length:{0}", strLen);
}
輪詢模式 Polling
利用 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 屬性來判斷異步方法調用是否完成。
public static void AsyncPollingTest()
{
MyDel myDel = new MyDel(TestClass.DelFun);
Console.WriteLine("主線程:AsyncPollingTest() in " + Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = myDel.BeginInvoke("sqh", null, null);
Console.WriteLine("主線程繼續執行... in " + Thread.CurrentThread.ManagedThreadId);
while (!iar.IsCompleted){
Thread.Sleep(1000);
Console.WriteLine("Waiting... AsyncMethod()");
}
int strLen = myDel.EndInvoke(iar);
Console.WriteLine("Str Length:{0}", strLen);
}
回調模式 Callback
調用 BeginInvoke 時提供回調方法,異步方法調用結束后,回調方法在 ThreadPool 中的異步線程上自動執行,並調用 EndInvoke 方法。
回調方法形如:void AsyncCallback(IAsyncResult iar);
public static void AsyncCallbackTest()
{
MyDel myDel = new MyDel(TestClass.DelFun);
Console.WriteLine("主線程:AsyncCallbackTest() in " + Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = myDel.BeginInvoke("sqh", CallbackMethod, myDel);
Console.WriteLine("主線程繼續執行... in " + Thread.CurrentThread.ManagedThreadId);
// ... 其他操作,獨立於異步線程
}
public static void CallbackMethod(IAsyncResult iar)
{
Console.WriteLine("異步方法執行完畢,開始執行回調方法 in " + Thread.CurrentThread.ManagedThreadId);
//AsyncResult ar = (AsyncResult)iar; // **推薦**
//MyDel myDel = (MyDel)ar.AsyncDelegate;
MyDel myDel = (MyDel)iar.AsyncState; // 這種調用方式 BeginInvoke 需要 myDel 參數
int result = myDel.EndInvoke(iar);
Console.WriteLine(result);
}
其他:當委托的方法列表中有多個方法時,可以利用 GetInvocationList() 方法得到 Delegate[] 數組,然后依次調用 BeginInvoke 方法,不同的方法在線程池的不同線程上運行!
[1]. 異步編程模式系列(1-6);
[2]. 異步方法調用模式總結; 異步調用四種方法;
async/await
幾點共識
- async和await共存
- async/await均不會立即發起新線程,直至遇到真正的異步方法(Task)
- await用於打分裂點,程序遇到await直接返回,異步任務完成后再執行await后的代碼
// qq ww 異步方法完成, 返回結果:GoCostThings
Console.WriteLine("qq");
var ret = FuncAsync();
Console.WriteLine("ww");
public static async Task<string> FuncAsync() {
var document = await GoCostThings();
Console.WriteLine("異步方法完成, 返回結果:" + document);
return document;
}
public static Task<string> GoCostThings() {
var task = Task.Run(() => {
Thread.Sleep(3000);
return "GoCostThings";
});
return task;
}
