async and await 简单的入门

如果有几个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;
}
时间: 2024-11-05 15:57:13

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

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

一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就具体看看编译器到底在背后帮我们做了哪些复杂的工作的. 二.同步代码存在的问题 对于同步的代码,大家肯定都不陌生,因为我们平常写的代码大部分都是同步的,然而同步代码却存在一个很严重的问题,例如我们向一个Web服务器发出一个

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

Egret Engnie 5.1.10 Egret Wing 4.1.5 一.Promise.async.await相关知识 Promise介绍 阮一峰 async函数 阮一峰 具体和详细的说明用法可以查看以上资料.说实话,从as3转过来,用这些语法真的不习惯... 二.Egret中promise的用法 Egret中原本使用监听回调的方式加载皮肤主题文件被修改成了promise的用法 原来的RES资源管理类,loadGroup也被替换成了Promise的用法.其他如getResAsync.loa

使用 Async 和 Await 的异步编程(C# 和 Visual Basic)[msdn.microsoft.com]

看到Microsoft官方一篇关于异步编程的文章,感觉挺好,不敢独享,分享给大家. 原文地址:https://msdn.microsoft.com/zh-cn/library/hh191443.aspx 通过使用异步编程,你可以避免性能瓶颈并增强应用程序的总体响应能力. 但是,编写异步应用程序的传统技术可能比较复杂,使它们难以编写.调试和维护. Visual Studio 2012 引入了一个简化的方法(即异步编程),该方法利用 .NET Framework 4.5 和 Windows 运行时中

使用 Async 和 Await 的异步编程 #Reprinted#

异步方法容易编写 string urlContents = await client.GetStringAsync(); 以下特征总结了使上面一个异步方法. 方法签名包含一个 Async 或async 修饰符. 异步方法的名称以“Async”后缀,按照约定,关闭. 返回类型为下列类型之一: Task<TResult>,如果您的方法具有操作个线程类型 TResult 的返回语句. Task,如果方法没有返回语句或具有返回语句不操作. 无效 (在 Visual Basic 中子 ),如果您编写一个

说说C#的async和await 解决卡顿问题 转

C# 5.0中引入了async 和 await.这两个关键字可以让你更方便的写出异步代码. 看个例子: 可以看到,async和await关键字只是把上面的代码变得更简单易懂而已. public class MyClass { public MyClass() { DisplayValue(); //这里不会阻塞 System.Diagnostics.Debug.WriteLine("MyClass() End."); } public Task<double> GetVal

Async和Await进行异步编程

使用Async和Await进行异步编程(C#版 适用于VS2015) 你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net Framework 4.5和Windows 运行时提供了异步支持.编译器做了开发者以前做的困难的工作,而且你的应用保持了类似于异步代码的逻辑结构.结果,你轻易地就获得了所有异步编程的优势. 异步提升响应 异步对于可能阻塞的活动是至关

c#之Async、Await剖析

c#之Async.Await剖析 探索c#之Async.Await剖析 2015-06-15 08:35 by 蘑菇先生, 1429 阅读, 5 评论, 收藏, 编辑 阅读目录: 基本介绍 基本原理剖析 内部实现剖析 重点注意的地方 总结 基本介绍 Async.Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下. APM方式,BeginGetRequestStream需要传入回调函数,线程碰到BeginXXX时会以非阻塞形式继续执行下面逻辑,完

[.NET 4.5] ADO.NET / ASP.NET 使用 Async 和 Await 异步 存取数据库

此为文章备份,原文出处(我的网站)  [.NET 4.5] ADO.NET / ASP.NET 使用 Async 和 Await 异步 存取数据库 http://www.dotblogs.com.tw/mis2000lab/archive/2014/05/08/ado.net4.5_async_await_20140508.aspx 以前的ADO.NET也能作  "异步"(Async,大陆说法:异步),可以参考 KKBruce 2009/11月的文章: SQLCOMMAND的异步行程

Promise,Async,await简介

Promise 对象 转载:http://wiki.jikexueyuan.com/project/es6/promise.html 基本用法 ES6 原生提供了 Promise 对象.所谓 Promise 对象,就是代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理. 有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数.此外,Promise 对象提供的接口,使得控制异步操作更加容易.Promise