共用方式為


使用 Async 和 Await 進行異步程序設計 (Visual Basic)

您可以使用異步程式設計來避免效能瓶頸,並增強應用程式的整體回應性。 不過,撰寫異步應用程式的傳統技術可能很複雜,因此難以撰寫、偵錯和維護。

Visual Studio 2012 引進了簡化的方法,非同步程式設計,可運用 .NET Framework 4.5 和更新版本中的非同步支援,以及 Windows 執行階段。 編譯程式會執行開發人員用來執行的困難工作,而您的應用程式會保留類似同步程式代碼的邏輯結構。 因此,您可以使用一小部分的努力,取得異步程序設計的所有優點。

本主題提供何時及如何使用異步程式設計的概觀,並包含包含詳細數據和範例的支持主題連結。

Async 可改善回應性

Asynchrony 對於可能封鎖的活動而言非常重要,例如當您的應用程式存取 Web 時。 有時 Web 資源的存取速度很慢或延遲。 如果這類活動在同步進程中遭到封鎖,整個應用程式必須等候。 在異步程式中,應用程式可以繼續執行其他不相依於 Web 資源的工作,直到潛在的封鎖工作完成為止。

下表顯示異步程序設計可改善回應性的一般區域。 .NET Framework 4.5 和 Windows 運行時間所列的 API 包含支援異步程序設計的方法。

應用程式區域 支援包含異步方法的 API
網路存取 HttpClientSyndicationClient
處理檔案 StorageFileStreamWriterStreamReaderXmlReader
處理圖像 MediaCaptureBitmapEncoderBitmapDecoder
WCF 程式設計 同步和異步操作

Asynchrony 對於存取 UI 線程的應用程式來說特別有價值,因為所有 UI 相關活動通常會共用一個線程。 如果同步應用程式中有任何進程遭到封鎖,則會封鎖所有進程。 您的應用程式可能會停止回應,而您可能會誤以為它已經失敗,其實它只是正在等待。

當您使用異步方法時,應用程式會繼續回應 UI。 例如,您可以調整或最小化視窗的大小,或者如果您不想等待應用程式完成,可以關閉應用程式。

異步方法相當於為設計異步作業時可供選擇的選項列表新增自動變速器。 也就是說,您會獲得傳統異步程序設計的所有優點,但開發人員所付出的努力要少得多。

異步方法更容易撰寫

Visual Basic 中的 AsyncAwait 關鍵詞是異步程序設計的核心。 藉由使用這兩個關鍵字,您可以利用 .NET Framework 或 Windows 執行階段中的資源,像建立同步方法一樣輕鬆地建立異步方法。 您使用 AsyncAwait 定義的異步方法稱為異步方法。

以下範例,顯示非同步方法。 程式碼中的幾乎所有內容看起來應該都非常熟悉。 批注會指出您新增建立異步所需的功能。

您可以在本主題結尾找到完整的 Windows Presentation Foundation (WPF) 範例檔案,您可以從 Async 範例:從「使用 Async 和 Await 進行異步程式設計」的範例下載範例。

' Three things to note about writing an Async Function:
'  - The function has an Async modifier.
'  - Its return type is Task or Task(Of T). (See "Return Types" section.)
'  - As a matter of convention, its name ends in "Async".
Async Function AccessTheWebAsync() As Task(Of Integer)
    Using client As New HttpClient()
        ' Call and await separately.
        '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
        '  - getStringTask stores the task we get from the call to GetStringAsync.
        '  - Task(Of String) means it is a task which returns a String when it is done.
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com/dotnet")
        ' You can do other work here that doesn't rely on the string from GetStringAsync.
        DoIndependentWork()
        ' The Await operator suspends AccessTheWebAsync.
        '  - AccessTheWebAsync does not continue until getStringTask is complete.
        '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
        '  - Control resumes here when getStringTask is complete.
        '  - The Await operator then retrieves the String result from getStringTask.
        Dim urlContents As String = Await getStringTask
        ' The Return statement specifies an Integer result.
        ' A method which awaits AccessTheWebAsync receives the Length value.
        Return urlContents.Length

    End Using

End Function

如果 AccessTheWebAsync 沒有任何可在呼叫 GetStringAsync 和等候完成之間執行的工作,您可以在下列單一語句中呼叫和等候來簡化程序代碼。

Dim urlContents As String = Await client.GetStringAsync()

下列特性摘要說明先前範例如何成為異步方法:

  • 方法簽章包含 Async 修飾詞。

  • 依慣例,異步方法的名稱會以 「Async」 後綴結尾。

  • 傳回型態是下列其中一種類型:

    • 如果您的方法有一個 return 陳述式,其中運算元的類型為 TResult,則使用 Task(Of TResult)
    • Task 如果您的方法沒有 return 語句,或 return 語句沒有操作數。
    • 如果您要撰寫異步事件處理程式,請使用Sub

    如需詳細資訊,請參閱本主題稍後的「返回類型和參數」。

  • 這個方法通常至少包含一個 await 表達式,該表達式會標示出在等候的異步操作完成之前方法不能繼續執行的位置。 同時,方法會暫停,而控件會傳回方法的呼叫端。 本主題的下一節說明暫停點會發生什麼事。

在異步方法中,您可以使用提供的關鍵詞和類型來指出您想要執行的動作,而編譯程式會執行其餘作業,包括追蹤當控件回到暫停方法中的等候點時,必須執行的動作。 某些例行程式,例如迴圈和例外狀況處理,在傳統異步程序代碼中可能很難處理。 在異步方法中,您可以像在同步解決方案中一樣撰寫這些元素,並解決問題。

如需舊版 .NET Framework 中異步的詳細資訊,請參閱 TPL 和傳統 .NET Framework 異步程序設計

異步方法中會發生什麼事

異步程式開發中最重要的是要理解控制流如何在不同的方法間傳遞。 下圖引導您完成此過程:

顯示追蹤異步程序的圖表。

圖表中的數字會對應至下列步驟:

  1. 事件處理程式會呼叫並等候 AccessTheWebAsync 異步方法。

  2. AccessTheWebAsync 會建立 HttpClient 實例,並呼叫 GetStringAsync 異步方法,以字串的形式下載網站的內容。

  3. GetStringAsync發生的事件會暫停其進展。 也許它必須等待網站下載或其他封鎖活動。 若要避免封鎖資源,GetStringAsync將控制權移交給其呼叫端AccessTheWebAsync

    GetStringAsync 會傳回 Task(Of TResult),其中 TResult 是字串,並將 AccessTheWebAsync 工作指派給 getStringTask 變數。 工作代表呼叫 GetStringAsync的持續程序,承諾在工作完成時產生實際字串值。

  4. 因為 getStringTask 尚未被等待, AccessTheWebAsync 可以繼續執行其他不依賴 GetStringAsync的最終結果的工作。 該工作是由對同步方法 DoIndependentWork的呼叫表示。

  5. DoIndependentWork 是同步方法,可執行其工作並傳回其呼叫端。

  6. AccessTheWebAsync 已經用盡了可以執行的工作,除非得到 getStringTask 的結果。 AccessTheWebAsync next 想要計算並傳回所下載字串的長度,但方法在方法具有字串之前無法計算該值。

    因此,AccessTheWebAsync 使用 await 運算符暫停其進度,並將控制權交予呼叫 AccessTheWebAsync 的方法。 AccessTheWebAsync 傳回 Task(Of Integer) 給呼叫者。 該任務承諾會生成一個整數結果,這是下載的字串長度。

    備註

    如果 GetStringAsync(因此 getStringTask) 在 AccessTheWebAsync 等候它之前完成,那麼控制權將保留在 AccessTheWebAsync。 如果呼叫的異步程式 (AccessTheWebAsync) 已完成,而且 AccessTheWebSync 不必等待最終結果,暫停再返回getStringTask的費用就會浪費。

    在呼叫端內(此範例中的事件處理程式),處理模式會繼續。 呼叫端可能會在等待結果AccessTheWebAsync之前,執行其他不依賴於該結果的工作,或者呼叫端可能會立即等待該結果。 事件處理程式正在等候 AccessTheWebAsync,並 AccessTheWebAsync 正在等候 GetStringAsync

  7. GetStringAsync 會完成併產生字串結果。 呼叫 GetStringAsync 不會以您預期的方式傳回字串結果。 (請記住,方法已在步驟 3 中傳回工作。相反地,字串結果會儲存在表示方法完成的工作中, getStringTask。 await 運算符會從 getStringTask擷取結果。 指定語句會將擷取的結果指派給 urlContents

  8. 當 有字串結果時 AccessTheWebAsync ,方法可以計算字串的長度。 接著,AccessTheWebAsync 的工作也會完成,而等候的事件處理程式可以繼續。 在主題結尾的完整範例中,您可以確認事件處理程式會擷取並列印長度結果的值。

如果您不熟悉異步程序設計,請花一分鐘的時間考慮同步和異步行為之間的差異。 同步方法會在工作完成時傳回 (步驟 5),但異步方法會在工作暫停時傳回工作值(步驟 3 和 6)。 當異步方法最終完成其工作時,工作會標示為已完成,如果有任何,則結果會儲存在工作中。

如需控制流程的詳細資訊,請參閱異步程式中的控制流程(Visual Basic)。

API 異步方法

您可能想知道在哪裡可以找到支援非同步程式設計的方法,例如 GetStringAsync。 .NET Framework 4.5 或更高版本包含許多使用 AsyncAwait的成員。 您可以透過成員名稱附加的「Async」後綴和返回類型為 TaskTask(Of TResult),來識別這些成員。 例如,類別System.IO.Stream包含方法CopyToAsyncReadAsyncWriteAsync,以及同步方法CopyToReadWrite

Windows 執行環境也包含許多方法,您可以在 Windows 應用程式中搭配 AsyncAwait 使用。 如需詳細資訊和範例方法,請參閱 在 C# 或 Visual Basic 中呼叫異步 API異步程式設計(Windows 運行時間應用程式)WhenAny:橋接 .NET Framework 與 Windows 運行時間

線程

非同步方法旨在進行不阻塞的操作。 Await異步方法中的表達式不會在等候的工作執行時封鎖目前的線程。 相反地,表達式會將方法的其餘部分註冊為接續,並將控制權傳回至異步方法的呼叫端。

AsyncAwait 關鍵詞不會造成其他線程建立。 異步方法不需要多線程處理,因為異步方法不會在其自己的線程上執行。 方法會在目前的同步處理內容上執行,而且只有在方法處於使用中狀態時,才會在線程上使用時間。 您可以使用 Task.Run 將 CPU 密集型工作移至背景執行緒,但背景執行緒無法協助處理只是等待結果可用的程序。

幾乎在每種情況下,基於 async 的非同步程式設計方法都比現有的方法更可取。 特別是,這種方法比 BackgroundWorker I/O 系結作業更好,因為程式代碼比較簡單,您不需要防範競爭條件。 與 Task.Run結合 時,異步程序設計比 BackgroundWorker CPU 系結作業更好,因為異步程式設計會將執行程式碼的協調詳細數據與傳送至線程集區的工作 Task.Run 分開。

Async 和 Await

如果您使用 Async 修飾詞來指定方法是異步方法,您可以啟用下列兩項功能。

  • 標示的異步方法可以使用 Await 來指定暫停點。 await 運算符會告訴編譯程式,在等候的異步程式完成之前,異步方法無法繼續超過該點。 同時,控制權會傳回至異步方法的呼叫者。

    表達式中 Await 異步方法的暫停不會構成方法的結束,而且 Finally 區塊不會執行。

  • 標記的異步方法本身可由呼叫它的方法等候。

異步方法通常包含一或多個運算子的 Await 出現次數,但缺少 Await 表達式並不會造成編譯程序錯誤。 如果異步方法不使用 Await 運算符來標記暫停點,則該方法會像同步方法一樣執行,不論使用 Async 修飾詞。 編譯程式會發出這類方法的警告。

AsyncAwait 是內容關鍵詞。 如需詳細資訊和範例,請參閱下列主題:

傳回類型和參數

在 .NET Framework 程序設計中,異步方法通常會傳回TaskTask(Of TResult)。 在異步方法內, Await 運算符會套用至從呼叫另一個異步方法傳回的工作。

如果方法包含指定某類型運算元的 Return 語句,您可以指定 Task(Of TResult) 作為返回類型TResult

如果方法沒有 return 語句,或者包含不返回運算元的 return 語句,您可以使用 Task 作為傳回型別。

下列範例示範如何宣告並呼叫傳回 Task(Of TResult)Task的方法:

' Signature specifies Task(Of Integer)
Async Function TaskOfTResult_MethodAsync() As Task(Of Integer)

    Dim hours As Integer
    ' . . .
    ' Return statement specifies an integer result.
    Return hours
End Function

' Calls to TaskOfTResult_MethodAsync
Dim returnedTaskTResult As Task(Of Integer) = TaskOfTResult_MethodAsync()
Dim intResult As Integer = Await returnedTaskTResult
' or, in a single statement
Dim intResult As Integer = Await TaskOfTResult_MethodAsync()

' Signature specifies Task
Async Function Task_MethodAsync() As Task

    ' . . .
    ' The method has no return statement.
End Function

' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statement
Await Task_MethodAsync()

每個傳回的工作都代表進行中的工作。 任務包含有關異步進程狀態的信息,最後結果要么是進程的最終結果,要么是在進程不成功時出現的例外狀況。

異步方法也可以是 Sub 方法。 此傳回型別主要用於需要傳回型別的事件處理程式定義。 異步事件處理程式通常做為異步程式的起點。

無法等候的異步 Sub 程序,呼叫端無法捕捉方法擲回的任何例外狀況。

異步方法無法宣告 ByRef 參數,但方法可以呼叫具有這類參數的方法。

如需詳細資訊和範例,請參閱 異步傳回型別 (Visual Basic) 。 如需如何在非同步方法中捕捉例外狀況的詳細資訊,請參閱 Try...Catch...Finally 語句

在 Windows 執行階段程式設計中的非同步 API 具有以下其中一種傳回類型,這些類型類似於任務:

如需詳細資訊和範例,請參閱 在 C# 或 Visual Basic 中呼叫異步 API

命名慣例

依照慣例,您會將 「Async」 附加至具有 Async 修飾詞的方法名稱。

您可以忽略事件、基類或介面合約建議不同名稱的慣例。 例如,您不應該重新命名常見的事件處理程式,例如 Button1_Click

相關主題和範例 (Visual Studio)

標題 說明 範例
逐步解說:使用 Async 和 Await 存取 Web (Visual Basic) 示範如何將同步 WPF 解決方案轉換成異步 WPF 解決方案。 應用程式會下載一系列網站。 異步範例:使用 Async 和 Await 進行異步程序設計 (Visual Basic)
如何:使用 Task.WhenAll 擴充異步逐步解說 (Visual Basic) Task.WhenAll 新增至上一個逐步解說。 使用 WhenAll 會同時啟動所有下載。
如何:使用 Async 和 Await 平行提出多個 Web 要求 (Visual Basic) 示範如何同時啟動數個工作。 異步範例:平行提出多個 Web 要求
異步傳回型別 (Visual Basic) 說明異步方法可以傳回的類型,並解釋每個類型適用的情況。
異步程式中的控制流程 (Visual Basic) 透過異步程式中連續的 await 運算式,詳細追蹤控制流程。 異步範例:異步程式中的控制流程
Fine-Tuning 您的異步應用程式 (Visual Basic) 示範如何將下列功能新增至您的異步解決方案:

- 取消異步工作或工作清單 (Visual Basic)
- 在一段時間後取消異步工作 (Visual Basic)
- 在完成一個異步工作之後取消剩餘的異步工作 (Visual Basic)
- 啟動多個異步任務並在它們完成時加以處理(Visual Basic)
異步範例:微調您的應用程式
在非同步應用程式中處理重入問題(Visual Basic) 示範如何處理正在執行的異步操作在運行時重新啟動的情況。
WhenAny:在 .NET Framework 與 Windows 運行時間之間橋接 示範如何在 Windows 運行時間中將 .NET Framework 的工作類型與 IAsyncOperations 橋接,以便搭配 Windows 運行時間方法使用 WhenAny 異步範例:在 .NET 與 Windows 運行時間之間橋接 (AsTask 和 WhenAny)
異步取消:在 .NET Framework 與 Windows 執行環境之間的橋接 示範如何在 Windows 運行時間中將 .NET Framework 的工作類型與 IAsyncOperations 橋接,以便搭配 Windows 運行時間方法使用 CancellationTokenSource 非同步範例:在 .NET 與 Windows 執行環境之間橋接(AsTask 和 Cancellation)
使用 Async 進行檔案存取 (Visual Basic) 列出並示範使用 async 和 await 來存取檔案的優點。
以工作為基礎的異步模式 (TAP) 描述 .NET Framework 中新的異步模式。 此模式是以 TaskTask(Of TResult) 類型為基礎。

完整範例

下列程式代碼是本主題討論的 Windows Presentation Foundation (WPF) 應用程式中MainWindow.xaml.vb檔案。 您可以從 Async 範例下載範例:「使用 Async 和 Await 進行異步程式設計」的範例


Imports System.Net.Http

' Example that demonstrates Asynchronous Programming with Async and Await.
' It uses HttpClient.GetStringAsync to download the contents of a website.
' Sample Output:
' Working . . . . . . .
'
' Length of the downloaded string: 39678.

Class MainWindow

    ' Mark the event handler with Async so you can use Await in it.
    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

        ' Call and await immediately.
        ' StartButton_Click suspends until AccessTheWebAsync is done.
        Dim contentLength As Integer = Await AccessTheWebAsync()

        ResultsTextBox.Text &= $"{vbCrLf}Length of the downloaded string: {contentLength}.{vbCrLf}"

    End Sub


    ' Three things to note about writing an Async Function:
    '  - The function has an Async modifier.
    '  - Its return type is Task or Task(Of T). (See "Return Types" section.)
    '  - As a matter of convention, its name ends in "Async".
    Async Function AccessTheWebAsync() As Task(Of Integer)

        Using client As New HttpClient()

            ' Call and await separately.
            '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
            '  - getStringTask stores the task we get from the call to GetStringAsync.
            '  - Task(Of String) means it is a task which returns a String when it is done.
            Dim getStringTask As Task(Of String) =
                client.GetStringAsync("https://learn.microsoft.com/dotnet")

            ' You can do other work here that doesn't rely on the string from GetStringAsync.
            DoIndependentWork()

            ' The Await operator suspends AccessTheWebAsync.
            '  - AccessTheWebAsync does not continue until getStringTask is complete.
            '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
            '  - Control resumes here when getStringTask is complete.
            '  - The Await operator then retrieves the String result from getStringTask.
            Dim urlContents As String = Await getStringTask

            ' The Return statement specifies an Integer result.
            ' A method which awaits AccessTheWebAsync receives the Length value.
            Return urlContents.Length

        End Using

    End Function

    Sub DoIndependentWork()
        ResultsTextBox.Text &= $"Working . . . . . . .{vbCrLf}"
    End Sub

End Class

另請參閱