Task異步編程


 

Task異步編程中,可以實現在等待耗時任務的同時,執行不依賴於該耗時任務結果的其他同步任務,提高效率。

1、Task異步編程方法簽名及返回值:

  a) 簽名有async 修飾符

  b) 方法名以 Async 結尾(良好的編碼習慣)

    根據約定,將“Async”追加到具有 async 修飾符的方法名稱。如果某一約定中的事件、基類或接口協定建議其他名稱,則可以忽略此約定。例如,你不應重命名常用事件處理程序,例如 btnOpen_Click。

  c) 返回類型如下:

  • 如果方法有操作數為TResult 類型的返回語句,則為 Task<TResult>;
  • 如果你的方法沒有返回語句或具有沒有操作數的返回語句,則為 Task;
  • 如果你編寫的是異步事件處理程序,則為 void。

  d) 方法通常包含至少一個 await 表達式,該表達式標記一個點,在該點上,直到等待的異步操作完成,方法才能繼續。 同時,將方法掛起,並且控制權將返回到方法的調用方。

舉例如下:

private static async Task<int> OperateAsync()

2、 舉例分析:

 

static void Main(string[] args)
{
    Test1.DoClickAsync();
    Console.WriteLine("不等待異步方法,直接執行");
    Console.ReadKey();
}

 

public class Test1
{
    public static async void DoClickAsync()
    {
        Task<int> result = OperateAsync();
        Console.WriteLine("從OperateAsync方法中控制權限返回調用方DoClickAsync");
        int resultValue = await result;
        Console.WriteLine("耗時操作結果" + resultValue.ToString());
    }

    private static async Task<int> OperateAsync()
    {
        HttpClient client = new HttpClient();

        //執行異步方法 GetStringAsync
        Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");

        //由於下面的非異步方法不依賴上面異步方法結果,因此可以先執行,假設在這里執行一些非異步的操作
        DoIndependentWork();

        //等待操作掛起方法 OperateAsync,直到 getStringTask 完成,OperateAsync 方法才會繼續執行
        //同時,控制將返回到 OperateAsync 方法的調用方,直到 getStringTask 完成后,將在這里恢復控制。
        //然后從 getStringTask 拿到字符串結果
        string urlContents = await getStringTask;

        Console.WriteLine("方法OperateAsync");
        //返回字符串的長度(int 類型)
        return urlContents.Length;

    }

    private static void DoIndependentWork()
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("主線程執行內容" + i);
            Thread.Sleep(1000);
        }
    }
}

 

3、步驟分析:

  • Main主函數,調用並等待異步方法DoClickAsync;
  • DoClickAsync方法調用並等待異步方法OperateAsync;
  • 異步方法OperateAsync調用HttpClient對象並調用它的GetStringAsync異步方法來下載網站內容
  • 假設GetStringAsync中發生了某種情況,該情況掛起了它的進程。可能必須等待網站下載或一些其他阻塞的活動。為避免阻塞資源,GetStringAsync 會將控制權出讓給其調用方 OperateAsync。GetStringAsync返回Task,其中TResult為字符串,並且OperateAsync將任務分配給getStringTask變量。該任務表示調用GetStringAsync的正在進行的進程,其中承諾當工作完成時產生實際字符串值。
  • 由於尚未等待getStringTask,因此,OperateAsync可以繼續執行不依賴於 GetStringAsync得出最終結果的其他任務。該任務由對同步方法 DoIndependentWork 的調用表示。
  • DoIndependentWork 是完成其工作並返回其調用方的同步方法。
  • OperateAsync 已完成工作,可以不受 getStringTask 的結果影響。 接下來,OperateAsync 需要計算並返回該下載字符串的長度,但該方法僅在具有字符串時才能計算該值。因此,OperateAsync 使用一個 await 運算符來掛起其進度,並把控制權交給調用 OperateAsync 的方法。OperateAsync將 Task<int> 返回至調用方。該任務表示對產生下載字符串長度的整數結果的一個承諾。

【備注】

(1)如果 GetStringAsync(即 getStringTask)在 OperateAsync 等待前完成,則控制權會保留在 OperateAsync 中。 如果異步調用過程 (getStringTask) 已完成,並且OperateAsync不必等待最終結果,則掛起然后返回到 OperateAsync,但這會造成成本的浪費。

(2)在調用方內部(DoClickAsync),處理模式將繼續。在等待結果前,調用方可以開展不依賴於OperateAsync結果的其他工作,否則就需等待片刻。DoClickAsync等待 OperateAsync,而 OperateAsync 等待 GetStringAsync。

  • GetStringAsync 完成並生成一個字符串結果。 字符串結果不是通過你預期的方式調用 GetStringAsync 所返回的。(請記住,此方法已在前面步驟中返回一個任務。)相反,字符串結果存儲在表示完成方法 getStringTask 的任務中。 await 運算符從 getStringTask 中檢索結果。賦值語句將檢索到的結果賦給 urlContents。
  • 當OperateAsync具有字符串結果時,該方法可以計算字符串長度。然后,OperateAsync 工作也將完成,並且等待程序DoClickAsync可繼續使用。

4、同步行為和異步行為差異:

  • 同步行為,當其工作完成時會返回一個同步方法;
  • 異步行為,當其工作掛起時,異步方法會返回一個任務值。在異步方法最終完成其工作時,任務會標記為已完成,而結果(如果有)將存儲在任務中。

5、詳談返回類型:

在.NET 中,異步方法通常返回 Task 或 Task<TResult>。在異步方法中,await運算符應用於通過調用另一個異步方法返回的任務。

1、如果方法包含指定類型 TResult 的操作數的return語句,則將 Task<TResult> 指定為返回類型。

2、如果方法不含任何 return 語句或包含不返回操作數的 return 語句,則將Task用作返回類型。

【備注】

每個返回的任務表示正在進行的工作。任務可封裝有關異步進程狀態的信息,如果未成功,則最后會封裝來自進程的最終結果或進程引發的異常。

3、異步方法還可以是具有void返回類型。該返回類型主要用於定義需要void返回類型的事件處理程序。異步事件處理程序通常用作異步程序的起始點。無法等待具有void返回類型的異步方法,並且一個void返回值的調用方無法捕獲該方法引發的任何異常。

下面的示例演示如何聲明並調用可返回 Task 或 Task<TResult> 的方法。

        static void Main(string[] args)
        {
            DoMethodAsync();
            Console.ReadKey();
        }

        private static async void DoMethodAsync()
        {
            //調用Method1Async
            /*方式一*/
            Task<Guid> t1 = Method1Async();
            Guid guid1 = t1.Result;
            Console.WriteLine("調用Method1Async方式一操作結果" + guid1.ToString());
            /*方式二*/
            Guid guid2 = await Method1Async();
            Console.WriteLine("調用Method1Async方式二操作結果" + guid2.ToString());

            //調用Method2Async
            /*方式一*/
            Task t2 = Method2Async();
            await t2;
            /*方式二*/
            await Method2Async();
        }

        //返回Task<TResult>類型
        static async Task<Guid> Method1Async()  //Task<Guid>
        {
            Console.WriteLine("調用Method1Async");
            var result = Guid.NewGuid();

            await Task.Delay(10000);

            //這里返回一個 Guid 的類型
            return result;
        }

        //返回Task類型
        static async Task Method2Async()  //Task
        {
            Console.WriteLine("調用Method2Async");
            //Do...

            await Task.Delay(10000);

            //Do...

            //這里沒有 return 語句
        }

 

6、總結:

1、標記的異步方法(通過使用 async 修飾符)可以使用 await 來指定懸掛點。await 運算符通知編譯器,異步方法只有直到等待的異步過程完成才能繼續通過該點。同時,控制權將返回至異步方法的調用方。

2、異步方法通常包含 await 運算符的一個或多個匹配項,但缺少 await 表達式不會導致編譯器錯誤。如果異步方法未使用 await 運算符標記懸掛點,則該方法將作為同步方法執行,不管異步修飾符(async)如何。編譯器將為此類方法發布一個警告。

7、參考:

http://www.cnblogs.com/liqingwen/p/5922573.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM