• 使用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;
    }

    这段代码比较简单,使用同步方式一个一个的获取UriData,然后进行统计。

    如果要使用异步方式一个一个的统计,那应该如何计算呢?

    我以前演示过一段丑陋的代码大致如下:

    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()) {
            statusText.Text = string.Format("Found {0} bytes ...", total);
            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

    也可以认为全部都下载完毕了。所以返回最终的结果。

    image

    image

    如果使用async await来实现的话,代码如下:

    public async Task<int> SumPageSizesAsync2(IList<Uri> 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
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Linux Shell脚本启动jar、关闭jar
    SpringBoot基于切面来拦截@PathVariable参数及抛出异常全局处理方法
    SpringBoot引用font awesome不显示问题的解决
    解决RestTemplate请求url出现301转发错误 301 Moved Permanently
    npm报错:Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 10.x
    npm 安装 chromedriver 失败的解决办法
    npm run dev报错 JS stacktrace(Node内存溢出)
    Mysql批量修改表字段名称为小写
    Ubuntu18 apt更换国内源 加快下载速度
    微信小程序如何实现支付宝支付?
  • 原文地址:https://www.cnblogs.com/LoveJenny/p/2235241.html
Copyright © 2020-2023  润新知