計算機中有些處理比較耗時。調用這種處理代碼時,調用方若是站在那裏苦苦等待,會嚴重影響程序性能。例如,某個程序啓動後若是須要打開文件讀出其中的數 據,再根據這些數據進行一系列初始化處理,程序主窗口將遲遲不能顯示,讓用戶感到這個程序怎麼等半天也不出來,太差勁了。藉助異步調用能夠把問題輕鬆化 解:把整個初始化處理放進一個單獨線程,主線程啓動此線程後接着往下走,讓主窗口瞬間顯示出來。等用戶盯着窗口犯呆時,初始化處理就在背後悄悄完成了。程 序開始穩定運行之後,還能夠繼續使用這種技巧改善人機交互的瞬時反應。用戶點擊鼠標時,所激發的操做若是較費時,再點擊鼠標將不會當即反應,整個程序顯得 很沉重。藉助異步調用處理費時的操做,讓主線程隨時恭候下一條消息,用戶點擊鼠標時感到輕鬆快捷,確定會對軟件產生好感。編程
異步調用用來處理從外部輸入的數據特別有效。假如計算機須要從一臺低速設備索取數據,而後是一段冗長的數據處理過程,採用同步調用顯然很不合算:計算機先 向外部設備發出請求,而後等待數據輸入;而外部設備向計算機發送數據後,也要等待計算機完成數據處理後再發出下一條數據請求。雙方都有一段等待期,拉長了 整個處理過程。其實,計算機能夠在處理數據以前先發出下一條數據請求,而後當即去處理數據。若是數據處理比數據採集快,要等待的只有計算機,外部設備能夠 連續不停地採集數據。若是計算機同時鏈接多臺輸入設備,能夠輪流向各臺設備發出數據請求,並隨時處理每臺設備發來的數據,整個系統能夠保持連續高速運轉。 編程的關鍵是把數據索取代碼和數據處理代碼分別歸屬兩個不一樣的線程。數據處理代碼調用一個數據請求異步函數,而後徑自處理手頭的數據。待下一組數據到來 後,數據處理線程將收到通知,結束 wait 狀態,發出下一條數據請求,而後繼續處理數據。windows
異步調用時,調用方不等被調方返回結果就轉身離去,所以必須有一種機制讓被調方有告終果時能通知調用方。在同一進程中有不少手段能夠利用,筆者經常使用的手段是回調、event 對象和消息。安全
回調方式很簡單:調用異步函數時在參數中放入一個函數地址,異步函數保存此地址,待有告終果後回調此函數即可以向調用方發出通知。若是把異步函數包裝進一個對象中,能夠用事件取代回調函數地址,經過事件處理例程向調用方發通知。多線程
event 是 windows 系統提供的一個經常使用同步對象,以在異步處理中對齊不一樣線程之間的步點。若是調用方暫時無事可作,能夠調用 wait 函數等在那裏,此時 event 處於 nonsignaled 狀態。當被調方出來結果以後,把 event 對象置於 signaled 狀態,wait 函數便自動結束等待,使調用方從新動做起來,從被調方取出處理結果。這種方式比回調方式要複雜一些,速度也相對較慢,但有很大的靈活性,能夠搞出不少花樣 以適應比較複雜的處理系統。app
藉助 windows 消息發通知是個不錯的選擇,既簡單又安全。程序中定義一個用戶消息,並由調用方準備好消息處理例程。被調方出來結果以後當即向調用方發送此消息,並經過 wparam 和 lparam 這兩個參數傳送結果。消息老是與窗口 handle 關聯,所以調用方必須藉助一個窗口才能接收消息,這是其不方便之處。另外,經過消息聯絡會影響速度,須要高速處理時回調方式更有優點。異步
若是調用方和被調方分屬兩個不一樣的進程,因爲內存空間的隔閡,通常是採用 windows 消息發通知比較簡單可靠,被調方能夠藉助消息自己向調用方傳送數據。event 對象也能夠經過名稱在不一樣進程間共享,但只能發通知,自己沒法傳送數據,須要藉助 windows 消息和 filemapping 等內存共享手段或藉助 mailslot 和 pipe 等通訊手段。函數
異步調用原理並不複雜,但實際使用時容易出莫名其妙的問題,特別是不一樣線程共享代碼或共享數據時容易出問題,編程時須要時時注意是否存在這樣的共享,並通 過各類狀態標誌避免衝突。windows 系統提供的 mutex 對象用在這裏特別方便。mutex 同一時刻只能有一個管轄者。一個線程放棄管轄權後,另外一線程才能接管。當某線程執行到敏感區以前先接管 mutex,使其餘線程被 wait 函數堵在身後;脫離敏感區以後當即放棄管轄權,使 wait 函數結束等待,另外一個線程便有機會光臨此敏感區。這樣就能夠有效避免多個線程進入同一敏感區。性能
因爲異步調用容易出問題,要設計一個安全高效的編程方案須要比較多的設計經驗,因此最好不要濫用異步調用。同步調用畢竟讓人更舒服些:無論程序走到哪裏, 只要死盯着移動點就能心中有數,不至於象異步調用那樣,總有一種四面受敵、惶惶不安的感受。必要時甚至能夠把異步函數轉換爲同步函數。方法很簡單:調用異 步函數後立刻調用 wait 函數等在那裏,待異步函數返回結果後再繼續往下走。this
C#提供了異步方法調用的功能,先建立一個委託,該委託的簽名要與須要異步執行的方法定義相匹配。仍是以代碼來講明。url
委託的聲明:
public delegate string AsyncDelegateGetPage( Uri uri , out string url );
調用方代碼:
AsyncCallback callback = new AsyncCallback( ProcessPage ); //回調函數聲明
AsyncDelegateGetPage ad = new AsyncDelegateGetPage( GetPageSource );//實例化委託類型
IAsyncResult ar = ad.BeginInvoke( this.ObtainWork() ,out url, callback , ad );//開始調用
ar.AsyncWaitHandle.WaitOne();//阻塞主線程,直到異步完成,用於多線程同步,通常能夠不須要。
BeginInvoke方法爲開始異步調用,其參數爲動態的,依據委託的簽名。上面的狀況,參數爲:Uri , out url,回調函數實例(可爲null),委託實例(可爲null)。即前面的幾個參數爲委託方法的參數,後面2個分別是回調函數實例和委託實例。參數委託 實例用於將該實例傳遞到回調函數中。注意回調函數的必須爲void ,而且參數爲IAsyncResult 類型。
委託的方法代碼:
public String GetPageSource( Uri uri , out string url ){
url = uri.ToString();
return this.GetPageSource( uri );
}
回調函數代碼:
void ProcessPage( IAsyncResult ar ){
AsyncDelegateGetPage andl = (AsyncDelegateGetPage)ar.AsyncState;
string url;
string source = andl.EndInvoke( out url, ar );
}
委託類型的EndInvoke方法,參數爲異步結果,IAsyncResult類型。執行該方法後,返回異步調用的結果。
以上是針對有回調函數的狀況。
原文:http://blog.csdn.net/Come_On_steven/article/details/4332249