C#中await/async閒說

自從C#5.0增長異步編程以後,異步編程愈來愈簡單,async和await用的地方愈來愈多,愈來愈好用,只要用異步的地方都是一連串的異步,若是想要異步編程的時候,須要從底層開始編寫,這樣後邊使用的時候就是異步,那麼底層是如何實現??咱們如何編寫高效率的異步方法??html

#瞭解基於任務的異步模式(TAP)

基於任務的異步編程模型 (TAP) 提供了異步代碼的抽象化,你只需像往常同樣將代碼編寫爲一連串語句便可,在開始調用的地方運行。例如:var task = method()①; await task②; 在①的時候開始運行可能尚未運行完,在②程序掛起等待運行完,中間怎麼運行的你不須要知道,編譯器會作若干操做的。當開啓多個任務的時候,像要他們都執行完,在執行其餘的時候,能夠await Task.WhenAll(task1,task2 .....);編程

#瞭解async/await

await 運算符應用於異步方法,在方法的執行中插入掛起點,直到所等待任務完成。使用async 和await定義異步方法不必定會建立新線程,當編譯器看到await關鍵字時,線程會掛起等待運行結束。
await 僅可用於由 async 關鍵字修改的異步方法中,使用 async 修飾符定義的方法一般包含一個或多個 await 表達式,使用await運算符的任務一般是實現[基於任務的異步模式(TAP)]的方法調用返回,返回值包括 Task、Task<TResult>、ValueTask 和 ValueTask<TResult> 對象的方法。多線程

# 調用 Task.Wait() 或者 Task.Result 馬上產生死鎖的充分條件

1. 調用 Wait() 或 Result 的代碼位於 UI 線程。
2. Task 的實際執行在其餘線程,且須要返回 UI 線程。
死鎖的緣由:UWP、WPF、Windows Forms 程序的 UI 線程都是單線程的。爲了不產生死鎖,你應該一條道走到黑, Async All the Way。或者.ConfigureAwait(false)異步

# ValueTask與Task的區別

7.0爲async新增的ValueTask的做用(若是沒有在Nuget上下載System.Threading.Tasks.Extensions,ValueTask就在這個庫中),ValueTask用於值類型的異步;Task爲引用類型的,每次須要分配空間。
例如:async

public async Task<int> CalculateSum(int a, int b) {
    if (a == 0 && b == 0)
    {
        return 0;
    }

    return await Task.Run(() => a + b);
}

當a,b=0的時候不會運行到task裏,這個時候返回task就形成了資源的浪費,修改成如下會效率更高異步編程

public async ValueTask<int> CalculateSum2(int a, int b)
{
    if (a == 0 && b == 0)
    {
        return 0;
    }

    return await Task.Run(() => a + b);
}

可是也不是說處處用ValueTask會好,當是引用類型的時候,用ValueTask,你須要關注更多的數據,這個時候用Task會更好。優化

# await/async原理分析

[AsyncStateMachine(typeof(Class1.<CalculateSum2>d__1))]
public ValueTask<int> CalculateSum2(int a, int b)
{
    Class1.<CalculateSum2>d__1 <CalculateSum2>d__;
    <CalculateSum2>d__.a = a;
    <CalculateSum2>d__.b = b;
    <CalculateSum2>d__.<>t__builder = AsyncValueTaskMethodBuilder<int>.Create();
    <CalculateSum2>d__.<>1__state = -1;
    AsyncValueTaskMethodBuilder<int> <>t__builder = <CalculateSum2>d__.<>t__builder;
    <>t__builder.Start<Class1.<CalculateSum2>d__1>(ref <CalculateSum2>d__);
    return <CalculateSum2>d__.<>t__builder.Task;
}

對CalculateSum2代碼解析,發現沒有await/async,原來又是編譯器提供的語法糖。ui

[__DynamicallyInvokable, DebuggerStepThrough, SecuritySafeCritical]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
    if (stateMachine == null)
    {
        throw new ArgumentNullException("stateMachine");
    }
    ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
    RuntimeHelpers.PrepareConstrainedRegions();
    try
    {
        ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
        stateMachine.MoveNext();
    }
    finally
    {
        executionContextSwitcher.Undo();
    }
}

對Start方法進行分析,能夠看出MoveNext,程序的運行其實仍是一步一步進行的,那麼await/async會不會建立一個線程,這卻是不必定,這個由線程池決定,那麼異步了不建立一個線程,怎麼異步的,這裏的異步多是運行在已經有的線程上。spa

其餘的多線程文章

1. C#中await/async閒說線程

2. .NET中並行開發優化

3. C# Task.Run 和 Task.Factory.StartNew 區別

4. C#中多線程的並行處理

5. C#中多線程中變量研究

相關文章
相關標籤/搜索