目錄
使用 AsyncEnumerator 類
AsyncEnumerator 的體系結構
使用 Wait 和 Inbox 計數器
線程安全變形
了解更多信息
在上期專欄 (並發事件: 通過 C# 簡化 APM) 中,,我講述了一些有關如何使用新的 C# 語言功能( 匿名方法、lambda 表達式和迭代器)簡化異步編程的理念。在專欄最後,我說明了如何借助 C# 迭代器 使用同步編程模型完成異步編程。圖 1 顯示了示例迭代器。不過,僅使用常規的 C# foreach 語句是無 法執行迭代器的代碼的,因為此代碼始終由調用 foreach 的線程執行,並且是連續執行,不會暫停來完 成異步 I/O 操作。
在本期專欄中,我將介紹我的 AsyncEnumerator 類,它可以智能地驅動迭代器,使不同的線程池線程 能夠在不同的時間執行代碼,我可以確保迭代器只有在完成異步 I/O 操作後才會執行下一次迭代。此外 ,我還會介紹 AsyncEnumerator 類的體系結構及其工作原理。
使用 AsyncEnumerator 類
下面是 AsyncEnumerator 類的定義:
public class AsyncEnumerator {
// Methods called by code outside of the iterator
public AsyncEnumerator();
public void Execute(IEnumerator<Int32> enumerator);
// Methods called by code inside the iterator
public AsyncCallback End();
public IAsyncResult DequeueAsyncResult();
}
AsyncEnumerator 類的用法相當簡單。首先,我們來介紹如何實現您的迭代器成員,然後再介紹如何 調用。
將迭代器成員定義為可以接受所需的任何參數,並添加一個附加參數,該參數是對 AsyncEnumerator 對象的引用。返回 Int32 集合時您的迭代器成員必須設為原型;換句話說,它的返回類型必須是 IEnumerator<Int32>。
然後,在迭代器成員內部,通過調用相應的 BeginXxx 方法啟動每個異步操作。您知道,如果調用 BeginXxx 方法,則必須將完成異步操作時應調用的方法的名稱傳遞給它。
您可以調用 AsyncEnumerator 對象的 End 方法,而不需要定義自己的方法。End 方法會返回一個 AsyncCallback 代理,此代理標識在 AsyncEnumerator 類中定義的私有方法。因此,每個異步操作完成 時,AsyncEnumerator 對象中的代碼都會得到通知。然後,此代碼會將已完成的操作的 IAsyncResult 對 象放到 List<IAsyncResult> 對象(我稱它為 inbox,即收件箱)中。
在您的代碼中,在調用 BeginXxx 方法之後,放置一個 yield return 語句,用於返回排隊等候的異 步操作數。在圖 1 中,因為我只調用了一個 BeginXxx 方法 (BeginRead),所以 yield return 語句返 回值 1。yIEld return 語句將暫停您的迭代器方法,並停止執行其中的代碼。
圖 1 C# 迭代器
private static IEnumerator<Int32> ApmPatternWithIterator(
AsyncEnumerator ae, String pathname) {
using (FileStream fs = new FileStream(pathname, FileMode.Open,
FileAccess.Read, FileShare.Read, 8192, FileOptions.Asynchronous)) {
Byte[] data = new Byte[fs.Length];
fs.BeginRead(data, 0, data.Length, ae.End(), null);
yIEld return 1;
Int32 bytesRead = fs.EndRead(ae.DequeueAsyncResult());
ProcessData(data);
}
}
稍後,我會提供一些讓 yield return 語句返回非 1 值的其他示例,但對於許多應用程序而言,還是 返回 1 比較合適。在 yield return 語句中指定的數字用於告知 AsyncEnumerator 對象需要完成多少個 異步操作才能恢復迭代器的執行。因此,當迭代器暫停時,您啟動的所有異步操作都會繼續完成。完成每 個異步操作後,AsyncEnumerator 的收件箱中都會增加一個條目。當收件箱中的項目數等於您在 yIEld return 語句中指定的數字時,AsyncEnumerator 對象將恢復迭代器的執行,並允許繼續執行其代碼。