private static IEnumerator<Int32> HtmlToFile(AsyncEnumerator ae, String url, String file) {
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();
Byte[] data = new Byte[1024];
using (FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, data.Length, FileOptions.Asynchronous)) {
fs.SetLength(webResponse.ContentLength);
while (true) {
webResponseStream.BeginRead(data, 0, data.Length, ae.End(), null);
yield return 1;
Int32 bytesRead = webResponseStream.EndRead(ae.DequeueAsyncResult());
if (bytesRead == 0) break;
fs.BeginWrite(data, 0, bytesRead, ae.End(), null);
yield return 1;
fs.EndWrite(ae.DequeueAsyncResult());
}
}
}
}
ae.End() 回傳 AsyncCallback,完成時將 IAsyncResult 入收件箱。ae.DequeueAsyncResult() 以 FIFO 取出結果供 EndXxx 使用。yield return 1 暫停等待單一作業;迴圈中連續 Read → yield → Write → yield 實現流水線非同步。yield break 於例外時終止迭代器。
AsyncEnumerator ae = new AsyncEnumerator();
ae.Execute(HtmlToFile(ae, "https://www.Wintellect.com/", "LocalFile.Html"));
迭代器各段代碼理論上可由不同執行緒池執行緒執行,**不得依賴執行緒區域儲存/UI 執行緒/優先權等執行緒特定狀態**。
目前 Execute 會阻塞呼叫執行緒直到迭代器結束,適合測試。WinForms/WPF 需從 UI 執行緒啟動且不阻塞,下一節將介紹非阻塞執行模式。