上一節日記中,項目所展示的非同步編程模式到底是怎麼實現的?
上一節代碼中定義了一個委託:
public delegate long CalculateFolderSizeDelegate(string FolderName);
經過編譯器,在編譯上述委託語句時,會自動產生以下的類模板:
public sealed class CalculateFolderSizeDelegate: System.MulticastDelegate
{
public CalculateFolderSizeDelegate(Object target , int methodPtr){.......}
public void virtual Invoke(string FolderName){........}
public virtual IAsyncResult BeginInvoke(
string FolderName , AsyncCallback callback , Object asyncState){...........}
publlic virtual void EndInvoke(IAsyncResult result){.........}
}
當使用普通的同步模式直接通過委託變數調用方法時:
CalculateFolderSizeDelegate d = CalculateFolderSize;
long size = d("C:\\Windows");
編譯器會將這一調用轉換為一個對應的Invoke方法調用。這是一個同步調用,與調用者代碼執行在峿 個線程中。
要想實現非同步呼叫,就不能 使用 Invoke方法,而必須使用 BeginInvoke 和 EndInvoke 方法。
一、關於BeginInvoke
編譯器為委託資料類型產生的BeginInvoke 方法是這個“模樣”:
public IAsyncResult BeginInvoke(<輸入和輸出變數>, AsyncCallback callback,object asyncState)
參數的第一部分是定義委託時確定的方法簽名中的參數列表,第二個參數callback是當非同步呼叫結束時自動回調的方法,第三個參數asyncState用於向第二個
參數所確定的callback回調方法提供額外的資訊,比如如果要回調的方法有參數,則asyncState可用於“填充”這些參數值。
這幾個參數將在後面的日記中詳細說明用法
需要注意的是:BeginInvoke方法返回一個IAsyncResult介面的對象。
此介面定義如下:
public interface IAsyncResult
{
object AsyncState {get;}
WaitHandle AsyncWaitHandle{get;}
bool CompletedSynchronously {get;]
bool IsCompleted{get;}
}
請注意,BeginInvoke方法中的第三個參數被原封不動地封裝成了IAsyncresult介面中的屬性 AsyncState.
另一個有趣的欄位是 IsCompleted,啟動非同步呼叫 的線程可以通過這個欄位查詢非同步呼叫是否完成。
bool類型的屬性 CompletedSynchronously指明 BeginInvoke調用是否同步完成。
另一個成員AsyncWaitHandle屬性是一個 "WaitHandle(等待控制代碼)"對象,當非同步作業完成時,此控制代碼居於 signaled狀態。
二、關於EndInvoke
編譯器為委託資料類型產生的 EndInvoke方法是這個模樣:
public <方法傳回值類型> EndInvoke( <聲明為ref或out參數> , IAsyncResult result)
前面介紹過,BeginInvoke方法負責產生一個 IAsyncResult 介面類型的對象並填充其欄位。
藉助於 IAsyncResult 對象,EndInvoke 方法不斷查詢非同步呼叫的方法是否執行完畢,根據
委託類型的定義,EndInvoke方法知道被非同步呼叫的方法所有的參數,因此當 EndInvoke方法發現非同步呼叫完成時,
它取出非同步呼叫方法執行的結果作為其傳回值,如果非同步呼叫方法有聲明為ref和out的參數,它也負責填充它。
這樣,啟動非同步呼叫的調用者線程就可以擷取非同步呼叫方法的結果了。
正由於EndInvoke 方法有一個不斷輪詢的過程,所以主線程程式在執行到調用 EndInvoke方法時全暫停,等待非同步呼叫方法完成,取回
結果後再繼續運行。