文章出處

接上文 多線程編程學習筆記——任務并行庫(一)

接上文 多線程編程學習筆記——任務并行庫(二)

 接上文 多線程編程學習筆記——任務并行庫(三)

  接上文 多線程編程學習筆記——任務并行庫(四)

 

        通過前面的文章,已經學習了怎么使用線程,怎么使用線程同步,怎么使用線程池,怎么使用任務并行庫。盡管通過上面的學習,對于線程的使用越來越簡單。有沒有更簡單的方法呢。

       C# 5.0之后,微軟在c#語言中添加了兩個關鍵字async與await,這是在TPL上面的更高一級的抽象,真正簡化了異步編程的編程方式,從而有助于我們編寫出真正健壯少bug的異步應用程序。下面我先來看一個最簡單的示例。

async Task<string> AsyncHello()
{

await Task.Delay(TimeSpan.FromSeconds(2));
Return “ Hello world”;
}

         使用async標記異步函數,建議返回async Task<T>。

         Await只能使用在有async標志的方法內部。在async標記的方法內部最少要有一個await,當然,如果一個也沒有,編譯也不會報錯,但是會有編譯警告。如下圖。

     

       上面的代碼在執行完await調用的代碼之行后該方法會直接返回。如果同步執行,執行線程會阻塞2秒之后返回結果,本示例里在執行完await操作后,立即將工作線程放回線程池中,我們會異步等待。2秒后,我們會從線程池中取得工作線程并繼續運行其中剩余的異步方法。這就允許我們在等待的2秒的時間里可以重用線程池中的線程,這對提高應用程序的可伸縮性非常重要。通過使用async與await我們擁有了線性的程序控制流程,但是執行過程卻是異步的。

 

一、   使用await獲取異步操作結果

        本示例是學習await如何獲取異步操作結果。同時會與TPL進行比較。

 1.示例代碼如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading; 

namespace ThreadAsyncDemo
{

    class Program
    {
        static void Main(string[] args)
        {

            Task t = AsyncWithTPL();
            t.Wait();
            t = AsyncWithAwait();
            t.Wait();
            Console.Read();
        }

 

        static Task AsyncWithTPL()
        {
            Task<string> task1 = GetInfoAsync("Task 1");

            Task task2 = task1.ContinueWith(task =>
              Console.WriteLine(task1.Result), TaskContinuationOptions.NotOnFaulted);
            Task task3 = task1.ContinueWith(task =>
              Console.WriteLine(task1.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
            return Task.WhenAny(task2, task1);

        }

        async static Task AsyncWithAwait()
        {
            try
            {
                string result = await GetInfoAsync("Task 4");
                Console.WriteLine(result);
            }

            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);

            }
        } 

        async static Task<string> GetInfoAsync(string name)
        {

            await Task.Delay(TimeSpan.FromSeconds(2));
            //throw  new Exception("拋出異常信息!"); 

            return string.Format(" Task {0} 正在運行在線程 ID={1}上。這個工作線程是否是線程池中的線程:{2}", name, 
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); } } }

 

 2.程序運行的結果如下圖。

 

         程序同時運行了兩個異步操作。其中一個是標準的TPL代碼,另一個使用了async與await兩個關鍵字。AsyncWithTPL啟動了一個任務,運行兩秒之后返回關于工作線程信息的字符串。然后我們定義了一個后續操作,用于在異步操作完成后打印出操作結果,還有另一個后續操作,用于萬一有錯誤時,打印出異常信息。最終返回了一個代表其中一個后續操作任務的任務,并等等其在主函數中完成。

        在asyncWithAwait方法中,我們對任務使用await并得到了相同 的結果。這和編寫普通的同步代碼的風格一樣,即我們獲取了任務的結果,打印了出來,如果任務完成時帶有錯誤則捕獲異常。關鍵不同的是這實際上是一個異步操作。使用await后,c#立即創建了一個任務,其中一個有后續操作任務,包含了await操作符后面的所有剩余代碼。這個新任務也處理了異常。然后這個任務返回 到主方法并等待共完成 。

       因此可以看出程序中的兩段代碼在概念上是相同的,使用await由編譯 器隱式地處理了異步代碼。

  3. 我們把上面注釋的拋出異常的代碼,取消注釋,然后運行程序。得到如下圖的結果。

 

            注:在gui與asp.net之類的環境 中不推薦 使用task.wait和taskResult方法同,因為如果代碼寫的不好,很容易導致死鎖。

 

二、   在Lambda表達式中使用await操作符

         本示例學習如何在lambda表達式中使用await。將學習如何編寫一個使用了await的匿名方法,并且獲取異步執行該方法的結果。

 1.示例代碼如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading; 

namespace ThreadAsyncDemo
{

    class Program
    {

        static void Main(string[] args)
        {

            Task t = AsyncProcess();
            t.Wait();       
            Console.Read();
        } 

        async static Task AsyncProcess()
        {

            Func<String, Task<string>> asyncLambda = async name =>
            {

                await Task.Delay(TimeSpan.FromSeconds(2));
                return string.Format(" Task {0} 正在運行在線程 ID={1}上。這個工作線程是線程池的線程:{2}" ,name, 
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); };
string result = await asyncLambda("async lambda"); Console.WriteLine(result); } } }

 

2.程序運行結果,如下圖。

 

        首先不能在main方法中使用async,我們將異步函數移到了asyncProcess中,然后使用async關鍵字聲明了一個lambda表達式。由于 任何lambda表達式的類型都不能通過lambda自身來推斷,所以不得不顯示地指定類型為一字符串,并返回一個Task<string>對象 。

        然后,我們定義 了lambda表達式體,這個方法雖然定義返回的是一個Task<string>對象 ,但實際上返回的是字符串,卻沒有編譯錯誤。這是因為c#編譯器自動 產生了一個任務并返回給我們。

       最后一步就是打印出lambda 表達式執行后的結果。

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()