• async and await 简单的入门


    如果有几个Uri,需要获取这些Uri的所有内容的长度之和,你会如何做?

     

    很简单,使用WebClient一个一个的获取uri的内容长度,进行累加。

    也就是说如果有5个Uri,请求的时间分别是:1s 2s 3s 4s 5s.

    那么需要的时间是:1+2+3+4+5=(6*5)/2=15.

    如果采用并行计算的话,结果可能是这样:

    image

    总时间长度是5s.

     

    为了演示效果,需要下面3个页面:

    image

    其中SlowPage 的Page_load代码如下:

    protected void Page_Load(object sender, EventArgs e)
    
    {
    
        Thread.Sleep(5000);
    
    }
    

    VerySlowPage的Page_load事件则 Thread.Sleep(10000);

    新建控制台程序CAStudy:

    首先新建类AsyncDemo:

    同步的获取Uris的内容长度代码如下:

    public class AsyncDemo
    
        {
    
            public int SumPageSizes(IList<Uri> uris)
    
            {
    
                int total = 0;
    
                foreach (var uri in uris)
    
                {
    
                    Console.WriteLine("Thread {0}:Found {1} bytes...{2}", 
    
                        Thread.CurrentThread.ManagedThreadId, total,DateTime.Now);
    
                    var data = new WebClient().DownloadData(uri);
    
                    total += data.Length;
    
                }
    
                Console.WriteLine("{0}:Found {1} bytes total {2}",
    
                    Thread.CurrentThread.ManagedThreadId, total, DateTime.Now);
    
                return total;
    
            }
    
        }
    

    在这里SumPageSizes 方法,通过foreach循环一个一个的下载数据

    Main函数如下:

    public static void Main()
    
    {
    
        List<Uri> uris = new List<Uri>();
    
        
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/QuickPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/SlowPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/VerySlowPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/QuickPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/SlowPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/VerySlowPage.aspx"));
    
        AsyncDemo asyncDemo = new AsyncDemo();
    
        int totalSize = asyncDemo.SumPageSizes(uris);
    
    }

    Main 函数主要是构造Uri,然后调用AsyncDemo的SumPageSizes方法来获取所有Uri的内容的总长度。

    结果如下:

    image

    可以看到时间分别是0s,5s,10s,0s ,5s,10s.所以总长度是(0+5+10)*2=30.

    可以看到速度很慢,如果有一个网页卡住的话,后面很恐怖的哦

    下面演示使用async,await的方式:

    第一步:将 VS2010 升级到 VS2010 sp1.

    第二步:下载Async CTP,进行安装

    第三步:为应用程序添加AsyncCTPLibrary引用,如下:

    image

    OK,将上面的SumPageSizes 方法修改如下:

    public async Task<int> SumPageSizesAsync2(IList<Uri> uris)
    
    {
    
        var tasks = uris.Select(uri => new WebClient().DownloadDataTaskAsync(uri));
    
        var data = await TaskEx.WhenAll(tasks);
    
        return await TaskEx.Run(() => 
    
        {
    
            return data.Sum(s => s.Length);
    
        });
    
    }

    在AsyncCTPLibrary.dll中,微软为一些类提供了扩展,如下:

    image

    WebClient的扩展如下:

    image

    可以看到基本上为每个Download 都增加了一个XXXTaskAsync 的扩展方法。

    返回的全部都是Task,

    为什么全部都是Task?,因为await 只能wait Task,并且await 只能用在async 标记的方法中,

    async 关键字表明这是个异步方法。

    第一句:

    public async Task<int> SumPageSizesAsync(IList<Uri> uris)

    因为我们申明的是一个异步方法,所以要使用async 关键字,SumPageSizesAsync方法返回的结果是int类型,所以返回Task<int>.

     

    第二句:

    IEnumerable<Task<Byte[]>> tasks = uris.Select(uri => new WebClient().DownloadDataTaskAsync(uri));

    获取DownloadDataTaskAsync返回的所有Task

    第三句:

    byte[][] data = await TaskEx.WhenAll(tasks);

    首先第二句返回的是IEnumerable<Task<Byte[]>> 类型,也就是一个一个的Task<Byte[]> 的任务,使用TaskExWhenAll方法可以将这些任务转变成一个Task<Byte[][]> 的任务

    使用await关键字意味着Task<Byte[][]> 方法需要等待,等待结束后返回Byte[][]

    第四句:

    return await TaskEx.Run<int>(() =>

                {

                    return data.Sum(s => s.Length);

                });

    TaskEx.Run 返回将使用第三句返回的data,将Byte[][] 的数据进行Sum运算,返回一个Task<int> 的对象,如果不使用await 的话:

    image

     

    因为 async 关键字代表的是异步方法,并且该异步方法返回的结果是int,所以需要再次使用await 关键字:

    return await TaskEx.Run<int>(() =>

                {

                    return data.Sum(s => s.Length);

                });

    修改Main代码如下:

    public static void Main()
    
    {
    
        List<Uri> uris = new List<Uri>();
    
        
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/QuickPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/SlowPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/VerySlowPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/QuickPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/SlowPage.aspx"));
    
        uris.Add(new Uri("http://localhost:57815/AsyncTestPages/VerySlowPage.aspx"));
    
        AsyncDemo asyncDemo = new AsyncDemo();
    
        Console.WriteLine(DateTime.Now);
    
        int totalSize = asyncDemo.SumPageSizesAsync(uris).Result;
    
        Console.WriteLine("TotalSize:{0}, Finished", totalSize);
    
        Console.WriteLine(DateTime.Now);
    
    }

    运行结果如下:

    image

    可以看到使用了16秒的时间,大致等于理论值15.

    有的同学会说,很麻烦!,的确,我也感觉很麻烦,还不如ThreadPool 来的快,不过async,await主要并不是解决这类问题的,它所解决的是异步中的同步,也就是说在某些异步操作中,需要同步的去处理,比如在Silverlight中,

    异步获取A –> 异步获取B –> 异步获取C..

    如果使用传统的方式则需要:

    WebClient webClient = new WebClient();
    
     webClient.DownloadDataCompleted += (s, e) =>
    
     { 
    
         // 使用A对象,做些事情。
    
         WebClient webClient2 = new WebClient();
    
         webClient2.DownloadDataCompleted += (s2, e2) =>
    
         {
    
             //使用B对象,做些事情。
    
         };
    
         webClient2.DownloadDataAsync(new Uri("B 的地址"));
    
     };
    
     webClient.DownloadDataAsync(new Uri("A 的地址"));

    当然在这里演示的是最丑陋的版本,聪明的同学可以使用Enumerable 来简化异步操作。

    如果使用async 和await则可以修改为:

    public async Task<int> SumPageSizesAsync3(IList<Uri> uris)
    
    {
    
        int total = 0;
    
        foreach (var uri in uris)
    
        { 
    
            WebClient webClient=new WebClient();
    
            var data = await webClient.DownloadDataTaskAsync(uri);
    
            total += data.Length;
    
        }
    
        return total;
    
    }
    作者:LoveJenny
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    mysql中标量子查询和关联子查询的性能比较
    关于mysql函数GROUP_CONCAT
    mysqldump和smbclient的简单使用
    找到自己除编码外可以做的事情
    smalldatetime和datetime的区别
    一个简单的职责链设计
    mysql中用HEX和UNHEX函数处理二进制数据的导入导出
    英语词根、词根、前缀、后缀大全
    ASP.NET编译执行常见错误及解决方法汇总之二
    ASP.NET编译执行常见错误及解决方法汇总之五(终结篇)
  • 原文地址:https://www.cnblogs.com/LoveJenny/p/2230933.html
Copyright © 2020-2023  润新知