《C#併發編程經典實例》學習筆記—異步編程關鍵字 Async和Await

C# 5.0 推出async和await,最先是.NET Framework 4.5引入,能夠在Visual Studio 2012使用。在此以前的異步編程實現難度較高,async使異步編程的實現變得簡便。編程

各平臺對async的支持狀況

平臺 async
.NET 4.5及以上
.NET 4.0 NuGet
Mono iOS/Droid
Windows Store
Windows Phone Apps 8.1
Windows Phone SL 8.0
Windows Phone SL 7.1 NuGet
Silverlight 5 NuGet

在不支持的平臺,安裝NuGet包 Microsoft.Bcl.Async數據結構

使用 async 修飾符可將方法、lambda 表達式或匿名方法指定爲異步。異步

async 對方法作了什麼處理

從使用async修飾符修飾的方法的IL代碼能夠得出一個結論:async

  • 在Debug下,針對async方法,生成的是一個class狀態機
  • 在Release下,針對async方法,生成的是一個struct狀態機

舉例:
C#代碼以下ide

using System.Threading.Tasks;

namespace ConsoleApp3
{
    public class Test
    {
        public async Task TestAsync()
        {
            await GetAsync();
        }

        public async Task GetAsync()
        {
            await Task.Delay(1);
        }
    }
}

以TestAsync方法爲準異步編程

Release下 初始化狀態機V_0 ,類型是值類型Struct(valuetype),類型名稱爲<TestAsync>d__0性能

.locals init (
      [0] valuetype ConsoleApp3.Test/'<TestAsync>d__0' V_0,
      [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
    )

<TestAsync>d__0 繼承值類型[mscorlib]System.ValueTypeui

.class nested private sealed auto ansi beforefieldinit 
    '<TestAsync>d__0'
      extends [mscorlib]System.ValueType
      implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine

Debug 下 初始化狀態機V_0 ,類型是引用類型Class(class) ,類型名稱爲<TestAsync>d__0this

.locals init (
      [0] class ConsoleApp3.Test/'<TestAsync>d__0' V_0,
      [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
    )

<TestAsync>d__0 繼承引用類型[mscorlib]System.Objectspa

.class nested private sealed auto ansi beforefieldinit 
    '<TestAsync>d__0'
      extends [mscorlib]System.Object
      implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine

異步方法的定義和注意事項

使用 async 關鍵字定義的異步方法簡稱爲「異步方法」。

注意事項:

  • 若是 async 關鍵字修改的方法不包含 await 表達式或語句,則該方法將同步執行。 編譯器警告將通知你不包含 await 語句的任何異步方法,由於該狀況可能表示存在錯誤。 請參閱編譯器警告(等級 1)CS4014
  • async 關鍵字是上下文關鍵字,緣由在於只有當它修飾方法、lambda 表達式或匿名方法時,它纔是關鍵字。 在全部其餘上下文中,都會將其解釋爲標識符。
  • 不要用 void 做爲 async 方法的返回類型! async 方法能夠返回 void ,可是這僅限於編寫事件處理程序。一個普通的 async 方法若是沒有返回值,要返回Task ,而不是 void
  • 必定要避免使用Task.WaitTask<T>.Result 方法,由於它們會致使死鎖。若是使用了 async ,最好就一直使用它。
  • 異步方法的參數不能使用outrefoutref 返回的數據應借用Task<TResult> 返回,可使用元組或自定義數據結構。

異步方法的特徵

  • 方法簽名包含 async 修飾符。
  • 按照約定,異步方法的名稱以「Async」後綴結尾。
  • 返回類型爲下列類型之一:
    • 若是你的方法有操做數爲 TResult 類型的返回語句,則爲 Task<TResult>
    • 若是你的方法沒有返回語句或具備沒有操做數的返回語句,則爲 Task
    • void:若是要編寫異步事件處理程序。
    • 包含 GetAwaiter 方法的其餘任何類型(自 C# 7.0 起)。
  • 方法一般包含至少一個 await 表達式,該表達式標記一個點,在該點上,直到等待的異步操做完成方法才能繼續。 同時,將方法掛起,而且控制返回到方法的調用方。

關於async和await具體的執行流程,方法什麼時候掛起和釋放,請參考異步程序中的控制流 (C#)

異步返回類型

上面提到 void 做爲返回結果,適用於事件處理程序。
舉例:

using System;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    public class TestVoidAsync
    {
        private event EventHandler<EventArgs> DoTest;

        public TestVoidAsync()
        {
            DoTest += DoTestEvent;
        }

        private static async void DoTestEvent(object sender, EventArgs e)
        {
            await Task.Delay(1000);
        }

        protected virtual void OnDoTest()
        {
            DoTest?.Invoke(this, EventArgs.Empty);
        }
    }
}

void 做爲返回結果存在一個弊端:沒法捕獲異常。

返回 void 的異步方法的調用方沒法捕獲從該方法引起的異常,且此類未經處理的異常可能會致使應用程序故障。 若是返回 TaskTask<TResult> 的異步方法中出現異常,此異常將存儲於返回的任務中,並在等待該任務時再次引起。

通用的異步返回類型:

從 C# 7.0 開始,異步方法可返回任何具備可訪問的 GetAwaiter 方法的類型。

ValueTask<TResult>

Task 和 Task<TResult> 是引用類型,所以,性能關鍵路徑中的內存分配會對性能產生負面影響,尤爲當分配出如今緊湊循環中時。 支持通用返回類型意味着可返回輕量值類型(而不是引用類型),從而避免額外的內存分配。

使用ValueTask<TResult>,須要添加NuGet包 System.Threading.Tasks.Extensions

ValueTask<TResult> 是struct值類型,Task 和 Task<TResult> 是class引用類型

異步操做的生命週期

Task 類提供了異步操做的生命週期,且該週期由 TaskStatus 枚舉表示。

狀態 執行順序 備註
Created 0 該任務已初始化,但還沒有安排。
WaitingForActivation 1 該任務正在等待被.NET Framework infrastructure 內部激活和調度。
WaitingToRun 2 該任務已安排執行但還沒有開始執行。
Running 3 任務正在運行但還沒有完成。
WaitingForChildrenToComplete 4 任務已完成執行,並隱式等待附加的子任務完成。
RanToCompletion 5 任務已成功完成執行。
Canceled 6 引起 OperationCanceledException 異常,或者在任務開始執行以前取消
Faulted 7 因爲未處理的異常,任務已完成。

Canceled 和 Faulted狀態都會由於任務異常致使轉換爲該狀態。兩者的區別以下:

若是標記的 IsCancellationRequested 屬性返回 false,或者異常的標記與任務的標記不匹配,則會將 OperationCanceledException 按照普通的異常來處理,從而致使任務轉換爲 Faulted 狀態。 另外還要注意,其餘異常的存在將也會致使任務轉換爲 Faulted 狀態。 您能夠在 Status 屬性中獲取已完成任務的狀態。

參考文章:

相關文章
相關標籤/搜索