天天看點

使用enumerator模式簡化異步操作

先看一段同步代碼:

public int SumPageSizes(IList<Uri> uris) { 

    int total = 0; 

    foreach (var uri in uris) { 

        statusText.Text = string.Format("Found {0} bytes ...", total); 

        var data = new WebClient().DownloadData(uri); 

        total += data.Length; 

    } 

    statusText.Text = string.Format("Found {0} bytes total", total); 

    return total; 

}

這段代碼比較簡單,使用同步方式一個一個的擷取Uri的Data,然後進行統計。

如果要使用異步方式一個一個的統計,那應該如何計算呢?

我以前示範過一段醜陋的代碼大緻如下:

WebClient webClient = new WebClient();

 webClient.DownloadDataCompleted += (s, e) =>

 {

     // 使用A對象,做些事情。

     WebClient webClient2 = new WebClient();

     webClient2.DownloadDataCompleted += (s2, e2) =>

     {

         //使用B對象,做些事情。

        // 遞歸的去 DownloadDataAsync。

     };

     webClient2.DownloadDataAsync(new Uri("B 的位址"));

 };

 webClient.DownloadDataAsync(new Uri("A 的位址"));

當然如果你确定隻有兩個位址的話,這種方法未嘗不可。如果有多個位址的話,則必須遞歸的調用了。

如何使用Enumerator來簡化異步操作:

public void SumPageSizesAsync(IList<Uri> uris) { 

    SumPageSizesAsyncHelper(uris.GetEnumerator(), 0); 

private void SumPageSizesAsyncHelper(IEnumerator<Uri> enumerator, int total) { 

    if (enumerator.MoveNext()) { 

        var client = new WebClient(); 

        client.DownloadDataCompleted += (sender, e) => { 

            SumPageSizesAsyncHelper(enumerator, total + e.Result.Length); 

        }; 

        client.DownloadDataAsync(enumerator.Current); 

    else { 

        statusText.Text = string.Format("Found {0} bytes total", total); 

        enumerator.Dispose(); 

通過SumPageSizesAsyncHelper ,可以實作異步調用A->異步調用B->異步調用C..的方式。

首先解釋下為什麼可以,假設uris 有A,B,C.

SumPageSizesAsyncHelper(uris.GetEnumerator(), 0);

方法先調用A,因為A後面還有B,是以enumerator.MoveNext()傳回True,

接着在DownloadDataCompleted事件結束後,調用B,同樣,因為B後面還有C,

是以enumerator.MoveNext() 繼續傳回True,接着在DownloadDataCompleted事件後調用C。

在調用C結束後,因為C後面沒有了,是以enumerator.MoveNext() 傳回False,

也可以認為全部都下載下傳完畢了。是以傳回最終的結果。

<a href="http://images.cnblogs.com/cnblogs_com/LoveJenny/201111/201111040633166391.png"></a>

<a href="http://images.cnblogs.com/cnblogs_com/LoveJenny/201111/201111040633191459.png"></a>

如果使用async 和await來實作的話,代碼如下:

public async Task&lt;int&gt; SumPageSizesAsync2(IList&lt;Uri&gt; uris)

{

    int total = 0;

    Char charText = 'A';

    foreach (var uri in uris)

    {

       var data = await new WebClient().DownloadDataTaskAsync(uri);

        total += data.Length;

        Console.WriteLine("Thread Id: {0}:調用{1}的位址 Found {2} bytes...{3}",

            Thread.CurrentThread.ManagedThreadId, charText, total, DateTime.Now);

        charText = Convert.ToChar(charText + 1);

    }

    Console.WriteLine("Thread Id: {0}:全部完成,Found {1} bytes total {2}",

        Thread.CurrentThread.ManagedThreadId, total, DateTime.Now);

    return total;

本文轉自LoveJenny部落格園部落格,原文連結:http://www.cnblogs.com/LoveJenny/archive/2011/11/04/2235241.html,如需轉載請自行聯系原作者