天天看点

使用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,如需转载请自行联系原作者