async and await 的入門基礎(chǔ)操作
如果有幾個(gè)Uri,需要獲取這些Uri的所有內(nèi)容的長度之和,你會(huì)如何做?
很簡(jiǎn)單,使用WebClient一個(gè)一個(gè)的獲取uri的內(nèi)容長度,進(jìn)行累加。
也就是說如果有5個(gè)Uri,請(qǐng)求的時(shí)間分別是:1s 2s 3s 4s 5s.
那么需要的時(shí)間是:1+2+3+4+5=(6*5)/2=15.
如果采用并行計(jì)算的話,結(jié)果可能是這樣:
總時(shí)間長度是5s.
為了演示效果,需要下面3個(gè)頁面:
其中SlowPage 的Page_load代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
Thread.Sleep(5000);
}
VerySlowPage的Page_load事件則 Thread.Sleep(10000);
新建控制臺(tái)程序CAStudy:
首先新建類AsyncDemo:
同步的獲取Uris的內(nèi)容長度代碼如下:
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循環(huán)一個(gè)一個(gè)的下載數(shù)據(jù)。
Main函數(shù)如下:
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 函數(shù)主要是構(gòu)造Uri,然后調(diào)用AsyncDemo的SumPageSizes方法來獲取所有Uri的內(nèi)容的總長度。
結(jié)果如下:
可以看到時(shí)間分別是0s,5s,10s,0s ,5s,10s.所以總長度是(0+5+10)*2=30.
可以看到速度很慢,如果有一個(gè)網(wǎng)頁卡住的話,后面很恐怖的哦
下面演示使用async,await的方式:
第一步:將 VS2010 升級(jí)到 VS2010 sp1.
第二步:下載Async CTP,進(jìn)行安裝
第三步:為應(yīng)用程序添加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中,微軟為一些類提供了擴(kuò)展,如下:
WebClient的擴(kuò)展如下:
可以看到基本上為每個(gè)Download 都增加了一個(gè)XXXTaskAsync 的擴(kuò)展方法。
返回的全部都是Task,
為什么全部都是Task?,因?yàn)閍wait 只能wait Task,并且await 只能用在async 標(biāo)記的方法中,
async 關(guān)鍵字表明這是個(gè)異步方法。
第一句:
public async Task<int> SumPageSizesAsync(IList<Uri> uris)
因?yàn)槲覀兩昝鞯氖且粋€(gè)異步方法,所以要使用async 關(guān)鍵字,SumPageSizesAsync方法返回的結(jié)果是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[]>> 類型,也就是一個(gè)一個(gè)的Task<Byte[]> 的任務(wù),使用TaskEx的WhenAll方法可以將這些任務(wù)轉(zhuǎn)變成一個(gè)Task<Byte[][]> 的任務(wù)
使用await關(guān)鍵字意味著Task<Byte[][]> 方法需要等待,等待結(jié)束后返回Byte[][]。
第四句:
return await TaskEx.Run<int>(() =>
{
return data.Sum(s => s.Length);
});
TaskEx.Run 返回將使用第三句返回的data,將Byte[][] 的數(shù)據(jù)進(jìn)行Sum運(yùn)算,返回一個(gè)Task<int> 的對(duì)象,如果不使用await 的話:
因?yàn)?async 關(guān)鍵字代表的是異步方法,并且該異步方法返回的結(jié)果是int,所以需要再次使用await 關(guān)鍵字:
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);
}
運(yùn)行結(jié)果如下:
可以看到使用了16秒的時(shí)間,大致等于理論值15.
有的同學(xué)會(huì)說,很麻煩!,的確,我也感覺很麻煩,還不如ThreadPool 來的快,不過async,await主要并不是解決這類問題的,它所解決的是異步中的同步,也就是說在某些異步操作中,需要同步的去處理,比如在Silverlight中,
異步獲取A –> 異步獲取B –> 異步獲取C..
如果使用傳統(tǒng)的方式則需要:
WebClient webClient = new WebClient();
webClient.DownloadDataCompleted += (s, e) =>
{
// 使用A對(duì)象,做些事情。
WebClient webClient2 = new WebClient();
webClient2.DownloadDataCompleted += (s2, e2) =>
{
//使用B對(duì)象,做些事情。
};
webClient2.DownloadDataAsync(new Uri("B 的地址"));
};
webClient.DownloadDataAsync(new Uri("A 的地址"));
當(dāng)然在這里演示的是最丑陋的版本,聰明的同學(xué)可以使用Enumerable 來簡(jiǎn)化異步操作。
如果使用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;
}
相關(guān)文章
C#中DropDownList動(dòng)態(tài)生成的方法
這篇文章主要介紹了C#中DropDownList動(dòng)態(tài)生成的方法,實(shí)例分析了C#中DropDownList的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03C#靜態(tài)變量與實(shí)例變量實(shí)例分析
這篇文章主要介紹了C#靜態(tài)變量與實(shí)例變量,是深入掌握PHP程序設(shè)計(jì)所必須掌握的重要概念,需要的朋友可以參考下2014-09-09DevExpress獲取TreeList可視區(qū)域節(jié)點(diǎn)集合的實(shí)現(xiàn)方法
這篇文章主要介紹了DevExpress獲取TreeList可視區(qū)域節(jié)點(diǎn)集合的實(shí)現(xiàn)方法,有一定實(shí)用價(jià)值,需要的朋友可以參考下2014-08-08C#中Convert.ToString和ToString的區(qū)別分析
這篇文章主要介紹了C#中Convert.ToString和ToString的區(qū)別,是C#初學(xué)者需要牢固掌握的技巧,需要的朋友可以參考下2014-08-08使用C#實(shí)現(xiàn)讀取PDF中所有文本內(nèi)容
這篇文章主要為大家詳細(xì)介紹了如何使用C#實(shí)現(xiàn)讀取PDF中所有文本內(nèi)容,文中的示例代碼簡(jiǎn)潔易懂,具有一定的學(xué)習(xí)價(jià)值,有需要的小伙伴可以了解下2024-02-02