源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

async and await 的入门基础操作

  • 时间:2020-04-13 13:53 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:async and await 的入门基础操作
[b]如果有几个Uri,需要获取这些Uri的所有内容的长度之和,你会如何做?[/b] [b][/b] 很简单,使用WebClient一个一个的获取uri的内容长度,进行累加。 也就是说如果有5个Uri,请求的时间分别是:1s 2s 3s 4s 5s. 那么需要的时间是:1+2+3+4+5=(6*5)/2=15. 如果采用并行计算的话,结果可能是这样: [img]http://files.jb51.net/file_images/article/201305/20130513162411127.png[/img] 总时间长度是5s. [b]为了演示效果,需要下面3个页面: [/b] [img]http://files.jb51.net/file_images/article/201305/20130513162411128.png[/img] 其中SlowPage 的Page_load代码如下:
[u]复制代码[/u] 代码如下:
protected void Page_Load(object sender, EventArgs e) {     Thread.Sleep(5000); }
VerySlowPage的Page_load事件则 Thread.Sleep(10000); 新建控制台程序CAStudy: 首先新建类AsyncDemo: 同步的获取Uris的内容长度代码如下:
[u]复制代码[/u] 代码如下:
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函数如下:
[u]复制代码[/u] 代码如下:
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的内容的总长度。 [b]结果如下:[/b] [img]http://files.jb51.net/file_images/article/201305/20130513162411129.png[/img]   可以看到时间分别是0s,5s,10s,0s ,5s,10s.所以总长度是[b](0+5+10)*2=30[/b]. 可以看到速度很慢,如果有一个网页卡住的话,后面很恐怖的哦 [b]下面演示使用async,await的方式:[/b] 第一步:将 VS2010 升级到 VS2010 sp1. 第二步:下载Async CTP,进行安装 第三步:为应用程序添加AsyncCTPLibrary引用,如下: [img]http://files.jb51.net/file_images/article/201305/20130513162411130.png[/img]   [b]OK,将上面的SumPageSizes 方法修改如下: [/b]
[u]复制代码[/u] 代码如下:
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中,微软为一些类提供了扩展,如下: [img]http://files.jb51.net/file_images/article/201305/20130513162411131.png[/img]   WebClient的扩展如下: [img]http://files.jb51.net/file_images/article/201305/20130513162411132.png[/img] 可以看到基本上为每个Download 都增加了一个XXXTaskAsync 的扩展方法。 返回的全部都是Task, [b]为什么全部都是Task?[/b],因为await 只能wait Task,并且await 只能用在async 标记的方法中, async 关键字表明这是个异步方法。 [b]第一句:[/b] public async Task<int> SumPageSizesAsync(IList<Uri> uris) 因为我们申明的是一个异步方法,所以要使用async 关键字,SumPageSizesAsync方法返回的结果是int类型,所以返回Task<int>. [b]第二句:[/b] IEnumerable<Task<Byte[]>> tasks = uris.Select(uri => new WebClient().DownloadDataTaskAsync(uri)); 获取DownloadDataTaskAsync返回的所有Task。 [b]第三句:[/b] byte[][] data = await TaskEx.WhenAll(tasks); 首先第二句返回的是IEnumerable<Task<Byte[]>> 类型,也就是一个一个的Task<Byte[]> 的任务,使用TaskEx的WhenAll方法可以将这些任务转变成一个Task<Byte[][]> 的任务 使用await关键字意味着Task<Byte[][]> 方法需要等待,等待结束后返回Byte[][]。 [b]第四句:[/b] return await TaskEx.Run<int>(() =>             {                 return data.Sum(s => s.Length);             }); TaskEx.Run 返回将使用第三句返回的data,将Byte[][] 的数据进行Sum运算,返回一个Task<int> 的对象,如果不使用await 的话: [img]http://files.jb51.net/file_images/article/201305/20130513162411133.png[/img] 因为 async 关键字代表的是异步方法,并且该异步方法返回的结果是int,所以需要再次使用await 关键字: return await TaskEx.Run<int>(() =>             {                 return data.Sum(s => s.Length);             }); 修改Main代码如下:
[u]复制代码[/u] 代码如下:
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); }
运行结果如下: [img]http://files.jb51.net/file_images/article/201305/20130513162411134.png[/img]   可以看到使用了16秒的时间,大致等于理论值15. 有的同学会说,很麻烦!,的确,我也感觉很麻烦,还不如ThreadPool 来的快,不过async,await主要并不是解决这类问题的,它所解决的是异步中的同步,也就是说在某些异步操作中,需要同步的去处理,比如在Silverlight中, 异步获取A –> 异步获取B –> 异步获取C.. 如果使用传统的方式则需要:
[u]复制代码[/u] 代码如下:
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则可以修改为:
[u]复制代码[/u] 代码如下:
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; }
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部