async and await 简单的入门

46 篇文章 0 订阅

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

 

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

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

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

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

总时间长度是5s.

 

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

其中SlowPage Page_load代码如下:

protectedvoid Page_Load(object sender, EventArgs e)

{

    Thread.Sleep(5000);

}

VerySlowPagePage_load事件则 Thread.Sleep(10000);

 

新建控制台程序CAStudy

首先新建类AsyncDemo:

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

publicclass AsyncDemo

    {

        publicint 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函数如下:

publicstaticvoid 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,然后调用AsyncDemoSumPageSizes方法来获取所有Uri的内容的总长度。

结果如下:

 

 

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

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

 

下面演示使用asyncawait的方式:

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

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

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

 

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中,微软为一些类提供了扩展,如下:

 

WebClient的扩展如下:

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

返回的全部都是Task

 

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

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

 

第一句:

publicasyncTask<int> SumPageSizesAsync(IList<Uri> uris)

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

 

第二句:

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

获取DownloadDataTaskAsync返回的所有Task

第三句:

byte[][] data = awaitTaskEx.WhenAll(tasks);

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

 

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

 

第四句:

returnawaitTaskEx.Run<int>(() =>

            {

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

            });

 

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

 

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

returnawaitTaskEx.Run<int>(() =>

            {

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

            });

 

修改Main代码如下:

publicstaticvoid 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);

}

 

运行结果如下:

 

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

有的同学会说,很麻烦!,的确,我也感觉很麻烦,还不如ThreadPool 来的快,不过asyncawait主要并不是解决这类问题的,它所解决的是异步中的同步,也就是说在某些异步操作中,需要同步的去处理,比如在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;

}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值