您的位置:首页 > 大数据 > 人工智能

async and await 简单的入门

2012-06-05 16:00 393 查看
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代码如下:

protectedvoid Page_Load(object sender, EventArgs e)
{

Thread.Sleep(5000);
}

VerySlowPage的Page_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,然后调用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
关键字表明这是个异步方法。

第一句:
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[]>
的任务,使用TaskEx的WhenAll方法可以将这些任务转变成一个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
来的快,不过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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: