等待運算子和 async 關鍵字

await 運算子和 async 關鍵字彙集在一起​​:

必須使用 async 關鍵字修改使用 await 的非同步方法。 ****

相反的情況並非總是如此:你可以在不使用 await 的情況下將方法標記為 async

await 實際上做的是暫停執行程式碼,直到等待的任務完成; 任何任務都可以等待。

注意: 你不能等待沒有返回任何內容的非同步方法(void)。

實際上,暫停這個詞有點誤導,因為不僅執行停止,而且執行緒可以自由執行其他操作。在引擎蓋下,await 由一些編譯器魔術實現:它將一個方法分為兩部分 - 在 await 之前和之後。當等待的任務完成時執行後一部分。

如果我們忽略一些重要的細節,編譯器大致會為你做這件事:

public async Task<TResult> DoIt()
{
    // do something and acquire someTask of type Task<TSomeResult>  
    var awaitedResult = await someTask;
    // ... do something more and produce result of type TResult
    return result;
}

變為:

public Task<TResult> DoIt()
{
    // ...
    return someTask.ContinueWith(task => {
        var result = ((Task<TSomeResult>)task).Result;
        return DoIt_Continuation(result);
    });
}

private TResult DoIt_Continuation(TSomeResult awaitedResult)
{
    // ...
}

可以通過以下方式將任何常用方法轉換為非同步:

await Task.Run(() => YourSyncMethod());

當你需要在 UI 執行緒上執行長時間執行的方法而不凍結 UI 時,這可能是有利的。

但是這裡有一個非常重要的評論: 非同步並不總是意味著併發(並行甚至多執行緒)。即使在單個執行緒上,async-await 仍然允許非同步程式碼。例如,請參閱此自定義任務計劃程式 。這種瘋狂的任務排程程式可以簡單地將任務轉換為在訊息迴圈處理中呼叫的函式。

我們需要問自己:什麼執行緒將執行我們的方法 DoIt_Continuation 的延續?

預設情況下,await 運算子使用當前的 Synchronization 上下文排程 continuation 的執行。這意味著預設情況下,WinForms 和 WPF 延續在 UI 執行緒中執行。如果由於某種原因需要更改此行為,請使用方法 Task.ConfigureAwait()

await Task.Run(() => YourSyncMethod()).ConfigureAwait(continueOnCapturedContext: false);