async and await 簡單的入門

若是有幾個Uri,須要獲取這些Uri的全部內容的長度之和,你會如何作?web

 

很簡單,使用WebClient一個一個的獲取uri的內容長度,進行累加。異步

也就是說若是有5個Uri,請求的時間分別是:1s 2s 3s 4s 5s.async

那麼須要的時間是:1+2+3+4+5=(6*5)/2=15.函數

若是採用並行計算的話,結果多是這樣:ui

 

總時間長度是5s.spa

 

爲了演示效果,須要下面3個頁面:對象

 

其中SlowPage 的Page_load代碼以下:事件

protected void Page_Load(object sender, EventArgs e)
{
    Thread.Sleep(5000);
}

VerySlowPage的Page_load事件則 Thread.Sleep(10000);同步

 

新建控制檯程序CAStudy:it

首先新建類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;
}  
相關文章
相關標籤/搜索