• 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
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Binary Tree Inorder Traversal
    Populating Next Right Pointers in Each Node
    Minimum Depth of Binary Tree
    Majority Element
    Excel Sheet Column Number
    Reverse Bits
    Happy Number
    House Robber
    Remove Linked List Elements
    Contains Duplicate
  • 原文地址:https://www.cnblogs.com/LoveJenny/p/2230933.html
Copyright © 2020-2023  润新知