圖 2 HtmlToFile 迭代器
private static IEnumerator<Int32> HtmlToFile(AsyncEnumerator ae,
String url, String file) {
// Issue asynchronous web request Operation
WebRequest webRequest = WebRequest.Create(url);
webRequest.BeginGetResponse(ae.End(), null);
yIEld return 1;
WebResponse webResponse;
try {
webResponse = webRequest.EndGetResponse(ae.DequeueAsyncResult());
}
catch (WebException e) {
Console.WriteLine("Failed to contact server: {0}", e.Message);
yIEld break;
}
using (webResponse) {
Stream webResponseStream = webResponse.GetResponseStream();
// Read the stream data and write it to a file in 1KB chunks
Byte[] data = new Byte[1024];
using (FileStream fs = new FileStream(file, FileMode.Create,
FileAccess.Write, FileShare.None, data.Length,
FileOptions.Asynchronous)) {
// See support.microsoft.com/kb/156932
fs.SetLength(webResponse.ContentLength);
while (true) {
// Issue asynchronous web response stream read Operation
webResponseStream.BeginRead(data, 0, data.Length,
ae.End(), null);
yIEld return 1;
// Get result of web response stream read Operation
Int32 bytesRead = webResponseStream.EndRead(
ae.DequeueAsyncResult());
if (bytesRead == 0) break; // Stream end: close file & exit
// Issue asynchronous file write Operation
fs.BeginWrite(data, 0, bytesRead, ae.End(), null);
yIEld return 1;
// Get result of file write Operation
fs.EndWrite(ae.DequeueAsyncResult());
}
}
}
}
在迭代器成員退出或執行 yIEld break 語句前,AsyncEnumerator 的 Execute 方法不會返回值。當 主線程受阻時,線程池線程將在異步操作完成時繼續執行迭代器。上述所有情形都意味著,迭代器的每部 分代碼理論上都可以由其他線程執行,因此迭代器代碼不應依賴於線程特定的狀態,如線程本地存儲、區 域、UI 區域、主體或線程優先級。
順便說一下,我並不傾向於讓線程直到迭代器完成運行時才調 用 Execute 塊。但這樣卻可以更簡單地說明 AsyncEnumerator 類的工作原理,並可以簡化試驗和調試。 但需要指出的是,有一種方法可以讓 AsyncEnumerator 在執行迭代器時不進行阻止。在 Windows® Form 或 Windows Presentation Foundation (WPF) 應用程序中,如果希望從 GUI 線程調用迭代器來獲 得可伸縮性和響應性,此方法將非常重要。在本專欄下一部分中,我將為您介紹如何異步執行迭代器成員 。