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

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

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

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

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

总时间长度是5s.

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

其中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的内容的总长度。

结果如下:

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

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

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

第一步:将 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 关键字表明这是个异步方法。

第一句:

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[]> 的任务,使用TaskEx的WhenAll方法可以将这些任务转变成一个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 的话:

因为 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);
}

运行结果如下:

可以看到使用了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;
}  

async and await 简单的入门的更多相关文章

  1. JavaScript中async和await的使用以及队列问题

    宏任务和微任务的队列入门知识,可以参考之前的文章: JavaScript的事件循环机制 宏任务和微任务在前端面试中,被经常提及到,包括口头和笔试题 async && await概念 a ...

  2. 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...

  3. 异步async/await简单应用与探究

    感谢Marco CAO指出的两点错误,已做出修改与补充 异步函数(async/await)简单应用 .NET Framework4.5提供了针对异步函数语法糖,简化了编写异步函数的复杂度. 下面通过一 ...

  4. 转:[你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单

    本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单  async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...

  5. [你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单

    本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单  async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...

  6. 四、C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 .NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就 ...

  7. 异步async与await的简单探究

    在学习.net core的过程中,到处见到异步的使用,Task.async.await随处可见.有点疑惑,就去了解了下这个过程是怎样的. 下面是一段代码,去看看是怎么执行的吧. 一.看看异步执行的方式 ...

  8. Promise、async、await在Egret的简单应用

    Egret Engnie 5.1.10 Egret Wing 4.1.5 一.Promise.async.await相关知识 Promise介绍 阮一峰 async函数 阮一峰 具体和详细的说明用法可 ...

  9. C# 异步编程,async与await的简单学习

    前提声明:C# 5.0 .NET Framework 4.5 2012-08-15 异步和等待(async和await).调用方信息(Caller Information)  (C#版本与.NET版本 ...

随机推荐

  1. Delphi中Frame的使用方法(1)

    Frame是组件面板上的第一个组件,但不是每个人都知道怎么用它,因为它不像Button和Label一样简单明了.实际上,Frame按钮只是打开一个Frame的列表,如果你没有创建任何的Frame,自然 ...

  2. JavaScript 创建类/对象的几种方式

    在JS中,创建对象(Create Object)并不完全是我们时常说的创建类对象,JS中的对象强调的是一种复合类型,JS中创建对象及对对象的访问是极其灵活的. JS对象是一种复合类型,它允许你通过变量 ...

  3. Lateral View使用指南

    https://blog.csdn.net/sunnyyoona/article/details/62894761 select sum(pitem) from (select map_values( ...

  4. Centos6.8配置svn

    svn的安装:yum -y install subversion 一.一个仓库放所有的项目 创建仓库,以后所有代码都放在这个下面,创建成功后在svn下面多了几个文件夹.1.创建仓库:svnadmin ...

  5. 转: codereview工具之 review board 选型与实践

    转:ReviewBoard代码评审实践总结 http://my.oschina.net/donhui/blog/350074 svn与review board 结合实践 http://my.oschi ...

  6. Android 5.0 怎样正确启用isLoggable(一)__使用具体解释

    isLoggable是什么 在Android源代码中,我们常常能够看到例如以下代码: //packages/apps/InCallUI/src/com/android/incallui/Log.jav ...

  7. tomcat通过虚拟路径访问外部静态资源

    转载:http://blog.csdn.net/yuancenyi/article/details/53414397 在项目开发中,单个工程中,为了以后软件版本升级的方便,经常将网站运行中某些上传的静 ...

  8. qq邮箱、qq空间点击后以word方式打开解决办法

    解决办法: Internet--工具--Internet选项--程序--设为默认值

  9. CodeForces 390E Inna and Large Sweet Matrix(树状数组改段求段)

    树状数组仅仅能实现线段树区间改动和区间查询的功能,能够取代不须要lazy tag的线段树.且代码量和常数较小 首先定义一个数组 int c[N]; 并清空 memset(c, 0, sizeof c) ...

  10. 02-spring学习-配置bean

    在spring的IOC容器中配置Bean 一,在xml中通过bean节点来配置bean: class:bean的类名,通过反射的方式在IOC容器中创建Bean,所以要求bean中必须有无参的构造器 i ...