使用 Async 和 Await 設計非同步程式 (C# 和 Visual Basic)
使用非同步程式設計,您可以避免效能瓶頸和增強應用程式的整體回應。不過,撰寫非同步應用程式的傳統技術可能會很複雜,使其無法寫入,偵錯和維護。
Visual Studio 2012 引入一種簡化的方法,非同步程式設計,支援在 .NET Framework 4.5 和 Windows 執行階段的非同步支援。編譯器已完成用來開發人員進行和應用程式保有邏輯結構類似同步處理程式碼的困難的工作。因此,您取得非同步程式設計的所有優點的一小部分工作。
本主題包含下列章節。
- Async 改善回應
- Async 方法更為容易。
- 要在非同步方法發生
- 應用程式開發介面 Async 方法
- 執行緒
- Async 和等候
- 參數和傳回型別
- 命名規範
- 相關主題
- 完整範例
- 相關主題
這個主題的時機和方式提供概觀使用非同步程式設計並包含連結支援包含詳細資料和範例的主題。
Async 改善回應
Asynchrony 對可能會封鎖的活動很重要,例如,當您的應用程式存取 Web 時。對網路資源的存取有時會變慢或延遲。如果此活動在同步處理的封鎖,整個應用程式必須等候。在非同步處理序,應用程式可以繼續不取決於 Web 資源的其他工作,直到可能封鎖工作完成。
下表顯示一般區域非同步程式設計位置改善回應。從 .NET Framework 4.5 中列出的 API 和 Windows 執行階段 包含支援非同步程式設計的方法。
應用程式範圍 |
包含支援非同步方法的 API |
---|---|
Web 存取 |
|
使用檔案。 |
|
與影像一起使用 |
|
WCF 程式設計 |
|
與通訊端一起使用 |
Asynchrony 證明特別重要存取 UI 執行緒的應用程式,因為所有 UI 相關活動通常共用一個執行緒。如果任何處理序中的應用程式被封鎖,所有被封鎖。您的應用程式停止回應,因此,您可能會認為,它失敗,在等候。
當您使用非同步方法時,應用程式會繼續回應 UI。您可以調整大小或最小化視窗,或者您可能結束應用程式,如果不要等待它完成。
這種以非同步的方式將自動傳輸的相當於可以選擇,當設計非同步作業期間選項的清單。您從開發人員取得傳統非同步程式設計的所有優點,但是使用較少工作。
Async 方法更為容易。
在 Visual Basic 中的 Async 和 等候 關鍵字和 非同步 和 等候 關鍵字在 C# 中為非同步程式設計的核心。您可以使用這兩個關鍵字,您在 .NET Framework 或 Windows 執行階段 中使用資源幾乎相同輕鬆建立非同步方法,就像建立同步方法。您定義了經由非同步並等候的非同步方法的非同步方法。
下列範例顯示一個非同步方法。幾乎可在程式碼所示完全熟悉您。註解召集功能加入至建立同步。
您可以完全範例檔本主題結尾,因此,您可以下載範例從 Async 範例:從「非同步程式設計的範例使用 Async 和等候」。
' Three things to note in the signature:
' - The method has an Async modifier.
' - The return type is Task or Task(Of T). (See "Return Types" section.)
' Here, it is Task(Of Integer) because the return statement returns an integer.
' - The method name ends in "Async."
Async Function AccessTheWebAsync() As Task(Of Integer)
' You need to add a reference to System.Net.Http to declare client.
Dim client As HttpClient = New HttpClient()
' GetStringAsync returns a Task(Of String). That means that when you await the
' task you'll get a string (urlContents).
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://msdn.microsoft.com")
' You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork()
' The Await operator suspends AccessTheWebAsync.
' - AccessTheWebAsync can't 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.
' Any methods that are awaiting AccessTheWebAsync retrieve the length value.
Return urlContents.Length
End Function
// Three things to note in the signature:
// - The method has an async modifier.
// - The return type is Task or Task<T>. (See "Return Types" section.)
// Here, it is Task<int> because the return statement returns an integer.
// - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't 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.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
如果 AccessTheWebAsync 沒有可以完成呼叫 GetStringAsync 並等候其完成之間的任何工作,您也可以呼叫和等候簡化您的程式碼在下列唯一陳述式。
Dim urlContents As String = Await client.GetStringAsync()
string urlContents = await client.GetStringAsync();
下列特性摘要列出執行上一個非同步方法。
方法簽章包含一個 Async 或 async 修飾詞。
非同步方法的名稱以「Async」後置字元,按照慣例,結束。
傳回型別為下列其中一種型別:
Task<TResult> ,如果您的方法具有運算元具有型別參數的 return 陳述式。
Task ,如果您的方法沒有傳回陳述式也沒有 return 陳述式沒有運算元。
void (Visual Basic 中為 子函數 ),如果您撰寫一個非同步事件處理常式。
如需詳細資訊,請參閱稍後的傳回型別和參數 > 主題。
方法通常至少包含一個等候運算式,表示點方法無法繼續,直到等候的非同步作業已完成。同時,方法暫停和控制項傳回到方法的呼叫端。本主題的下一節會說明為何發生在回應掛點。
在非同步方法,您可以使用提供的關鍵字和型別表示您想要,,而編譯器進行重設,包括記錄哪些必須發生時,控制項會在暫停的方法來等候點。一些常式處理,例如迴圈和例外狀況處理,很難處理以傳統非同步程式碼。在非同步方法,您可以撰寫這些項目就像您會在一個同步處理方案做的一樣,解決這個問題。
如需同步的更多有關在舊版 .NET Framework,請參閱 TPL 和傳統 .NET 非同步程式設計。
要在非同步方法發生
若要了解這個最重要的事在非同步程式設計是控制流程如何從方法移動到方法。下圖將程序解說。
在圖表上的數字對應到下列步驟。
事件處理常式呼叫並等候 AccessTheWebAsync 非同步方法。
AccessTheWebAsync 建立 HttpClient 的執行個體並呼叫 GetStringAsync 非同步方法下載網站的內容當做字串。
暫止其進度的值在 GetStringAsync 中。可能必須等待網站下載或其他封鎖活動。若要避免封鎖資源, GetStringAsync 產生控制項給它的呼叫端,則為 AccessTheWebAsync。
GetStringAsync 傳回 TResult 是字串的 Task<TResult> 和 AccessTheWebAsync 指定工作對 getStringTask 變數。當工作完成時,工作代表呼叫的持續性處理序對 GetStringAsync,以認可產生實際字串值。
因為 getStringTask 不等候, AccessTheWebAsync 可以繼續不取決於 GetStringAsync的最終結果的其他工作。該工作是由同步方法呼叫 DoIndependentWork表示。
DoIndependentWork 完成其工作並傳回它的呼叫端的同步方法。
AccessTheWebAsync 已用盡。它可以完成,而不需要從 getStringTask之結果的工作。AccessTheWebAsync 中要計算和傳回下載字串的長度,但是,方法無法計算該值,直到方法有字串。
因此, AccessTheWebAsync 使用 Await 運算子暫止其進度和產生控制項呼叫 AccessTheWebAsync的方法。AccessTheWebAsync 傳回 Task(Of Integer) 或 Task<int> 到呼叫端。工作表示承諾產生下載的字串長度的整數結果。
注意事項 如果 GetStringAsync ( getStringTask) 是完整的,在 AccessTheWebAsync 等候它之前,控制項仍為 AccessTheWebAsync。成本暫停然後傳回的 AccessTheWebAsync 將浪費,如果這個呼叫的非同步處理序 (getStringTask) 已完成,並 AccessTheWebSync 無需等候最終結果。
在呼叫端 (在這個範例中的事件處理常式內),則會重複項目。呼叫端可能會完成不取決於 AccessTheWebAsync 的結果在等候該結果之前的其他工作,或呼叫端可能會立即等候。當事件處理常式達到等候運算式時,應用程式將焦點放在 GetStringAsync完成。事件處理常式等候,則為 AccessTheWebAsync,且 AccessTheWebAsync 等候 GetStringAsync。
GetStringAsync 完成並產生字串結果。字串結果不會經由對 GetStringAsync 的呼叫傳回就像您預期的。(請記住方法還會傳回在第 3. 步驟) 的工作,字串結果表示方法的完成的工作, getStringTask中。等候運算子從 getStringTask擷取結果。指派陳述式 (Assignment 所擷取的結果為 urlContents。
當 AccessTheWebAsync 為結果時,方法可以計算字串的長度。然後 AccessTheWebAsync 工作也是完整的,因此,等候事件處理常式可以復原。在完整範例中本主題結尾,您可以檢查事件處理常式會擷取並列印長度結果。
如果您不熟悉非同步程式設計中,需要一分鐘考慮同步和非同步行為之間的差異。一個同步方法,會傳回其工作完成 (步驟 5),不過,非同步方法傳回的值,在其工作暫停時 (步驟 3 和步驟 6)。當非同步方法最後完成其工作時,工作會標示為已完成,而結果,如果有的話,在工作中。
如需控制流程的詳細資訊,請參閱 非同步程式中的控制流程 (C# 和 Visual Basic)。
應用程式開發介面 Async 方法
何處可能想知道尋找支援非同步程式設計的方法 (例如 GetStringAsync )。.NET Framework 4.5 包含與非同步一起使用並等待的許多成員。您也可以藉由「Async」後置字元辨認這些成員,那是附加至成員名稱和 Task 或 Task<TResult>之傳回型別。例如, System.IO.Stream 類別包含的方法 (例如、和 CopyToAsyncReadAsyncWriteAsync 沿著同步方法 CopyTo、 Read和 Write。
Windows 執行階段 也包含您在 Windows 市集 應用程式中使用非同步和等候的許多方法。如需詳細資訊和範例方法,請參閱 快速入門:使用非同步程式設計的 Await 運算子, 非同步程式設計 (Windows 市集應用程式)以及 WhenAny:銜接 .NET Framework 和 Windows 執行階段 (C# 和 Visual Basic)。
執行緒
非同步方法主要是用於未封鎖作業。在等待中工作正在執行時,在非同步方法的一個等候運算式不會封鎖目前的執行緒。相反地,運算式會以參與方法的其餘部分做為繼續並將控制項傳回到非同步方法的呼叫端。
非同步和 await 關鍵字不會導致其他執行緒建立。因為非同步方法本身不會執行執行緒,因此非同步方法不需要多執行緒。只有在方法作用中時,方法會在目前的同步處理內容執行並在執行緒上花費時間。您可以使用 Task.Run 移動 CPU-bound 工作移到背景執行緒,不過,背景執行緒不協助等待結果變成可使用的處理序。
對非同步程式設計的架構非同步的方法是更好。在幾乎每個案例的現有方法。請特別注意,這個方法比 IO 繫結作業的 BackgroundWorker 好,因為程式碼比較簡單,而且您不需要防止競爭情形。與 Task.Run搭配,非同步程式設計的 CPU 繫結作業的 BackgroundWorker 好,因為非同步程式設計分開執行您的程式碼協調詳細資料從 Task.Run 傳輸至 Threadpool 的工作。
Async 和等候
如果您指定使用 Async 或 非同步 修飾詞,方法是 async 方法,您可啟用下列兩個功能。
資訊清單 async 方法可以使用 等候 或 等候 指定回應掛點。等候運算子告訴編譯器 async 方法無法繼續按過去,直到這個等候的非同步處理序完成。同時,控制項會返回非同步方法的呼叫端。
執行非同步方法的停止在等候運算式並不會構成從方法的匯出,,且 finally 區塊不會執行。
資訊清單 async 方法本身可以用呼叫其方法等候。
非同步方法通常包含 Await 運算子的一或多個事件,不過,沒有等候運算式不會造成編譯器錯誤。如果非同步方法不使用 Await 運算子指示回應掛點,方法儘管 async 修飾詞執行,將同步方法,。編譯器將對這類程式碼發出警告。
Async、 async、 Await和 await 是內容關鍵字。如需詳細資訊和範例,請參閱下列主題:
參數和傳回型別
以程式設計的 .NET Framework,非同步方法通常會傳回 Task 或 Task<TResult>。在非同步方法內, Await 運算子套用至從呼叫傳回至另一個非同步方法的工作。
您指定 Task<TResult> 為傳回型別,如果方法包含一個 TResult運算元 傳回 (Visual Basic) 或 傳回 (C#) 陳述式。
您可以使用 Task ,因為傳回型別,如果方法沒有傳回陳述式也不會傳回運算元的 return 陳述式。
下列範例顯示如何宣告及呼叫傳回 Task<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()
// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
int hours;
// . . .
// Return statement specifies an integer result.
return hours;
}
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
// Signature specifies Task
async Task Task_MethodAsync()
{
// . . .
// The method has no return statement.
}
// Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
// or, in a single statement
await Task_MethodAsync();
每個傳回的工作代表持續性的工作。工作封裝有關這個非同步處理序狀態的相關資訊,以及最後,從處理序的結果或例外狀況處理序時,如果不成功。
非同步方法也可以是 Sub 方法 (Visual Basic) 或具有 void 傳回型別 (C#)。這個傳回型別主要用於定義事件處理常式,則需要 void 傳回型別。非同步事件處理常式通常起點做為非同步程式。
是 Sub 程序的非同步方法或具有 void 無法等待回覆的傳回型別,和void-returning呼叫端方法無法攔截方法擲回的任何例外狀況。
非同步方法不可以宣告為在 Visual Basic中的ByRef或ref或out 參數在 C# 中,但此方法可以呼叫有該參數的方法。
如需詳細資訊與範例,請參閱非同步方法的傳回型別 (C# and Visual Basic)。如需如何攔截發生在非同步方法的例外狀況的詳細資訊,請參閱 try-catch (C# 參考) 或 Try...Catch...Finally 陳述式 (Visual Basic)。
在 Windows 執行階段 程式設計的非同步 API 會傳回下列其中一種型別,類似工作:
IAsyncAction,對應於 Task。
如需詳細資訊和範例,請 快速入門:使用非同步程式設計的 Await 運算子參閱。
命名規範
依照慣例,您附加「Async」對具有 Async 或 async 修飾詞方法的名稱。
您可以忽略事件、基底型別或介面合約建議不同名稱的慣例。例如,您不應該重新命名通用事件處理常式時,例如 Button1_Click。
相關主題
標題 |
描述 |
範例 |
---|---|---|
說明如何將轉換的 WPF 方案至非同步 WPF 方案。應用程式下載一系列的網站。 |
||
將 Task.WhenAll 附加到前一個逐步解說。使用 WhenAll 同時啟動所有下載。 |
||
示範如何同時啟動數個工作。 |
||
說明非同步方法可以傳回的型別並說明每種型別時適用。 |
||
追蹤控制流程可以執行詳細等候在非同步程式的運算式。 |
||
顯示如何將下列功能加入至您的非同步方案: |
||
顯示如何處理現用非同步作業已重新啟動的情況下,當它執行時。 |
||
WhenAny:銜接 .NET Framework 和 Windows 執行階段 (C# 和 Visual Basic) |
顯示如何橋接在工作之間輸入 .NET Framework 和 IAsyncOperations 在 Windows 執行階段 中,使您可以使用與 Windows 執行階段WhenAny 方法。 |
|
顯示如何橋接在工作之間輸入 .NET Framework 和 IAsyncOperations 在 Windows 執行階段 中,使您可以使用與 Windows 執行階段CancellationTokenSource 方法。 |
||
列出並示範非同步的優點和等候存取檔案。 |
||
示範在等候陳述式的控制流程,並示範 [逐步執行]、 [不進入函式] 和 [跳離函式] 命令的行為在非同步方法內。 |
||
說明 .NET Framework 中 asynchrony 的新的樣式。這個樣式會根據 Task 和 Task<TResult> 型別。 |
||
在 Windows 市集 應用程式會示範如何使用非同步和等候。 |
||
提供 Windows 執行階段中的非同步程式設計概觀。 |
||
提供有關非同步程式設計的各種不同的視訊。 |
完整範例
下列程式碼是從這個主題討論 Windows Presentation Foundation (WPF) 應用程式之 MainWindow.xaml.vb 或 MainWindow.xaml.cs 檔案。您可以下載範例從 Async 範例:從「非同步程式設計的範例使用 Async 和等候」。
' Add an Imports statement and a reference for System.Net.Http
Imports System.Net.Http
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 separately.
'Task<int> getLengthTask = AccessTheWebAsync();
'' You can do independent work here.
'int contentLength = await getLengthTask;
Dim contentLength As Integer = Await AccessTheWebAsync()
ResultsTextBox.Text &=
String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
End Sub
' Three things to note in the signature:
' - The method has an Async modifier.
' - The return type is Task or Task(Of T). (See "Return Types" section.)
' Here, it is Task(Of Integer) because the return statement returns an integer.
' - The method name ends in "Async."
Async Function AccessTheWebAsync() As Task(Of Integer)
' You need to add a reference to System.Net.Http to declare client.
Dim client As HttpClient = New HttpClient()
' GetStringAsync returns a Task(Of String). That means that when you await the
' task you'll get a string (urlContents).
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://msdn.microsoft.com")
' You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork()
' The Await operator suspends AccessTheWebAsync.
' - AccessTheWebAsync can't 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.
' Any methods that are awaiting AccessTheWebAsync retrieve the length value.
Return urlContents.Length
End Function
Sub DoIndependentWork()
ResultsTextBox.Text &= "Working . . . . . . ." & vbCrLf
End Sub
End Class
' Sample Output:
' Working . . . . . . .
' Length of the downloaded string: 41763.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// Add a using directive and a reference for System.Net.Http;
using System.Net.Http;
namespace AsyncFirstExample
{
public partial class MainWindow : Window
{
// Mark the event handler with async so you can use await in it.
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
// Call and await separately.
//Task<int> getLengthTask = AccessTheWebAsync();
//// You can do independent work here.
//int contentLength = await getLengthTask;
int contentLength = await AccessTheWebAsync();
resultsTextBox.Text +=
String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
}
// Three things to note in the signature:
// - The method has an async modifier.
// - The return type is Task or Task<T>. (See "Return Types" section.)
// Here, it is Task<int> because the return statement returns an integer.
// - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't 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.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
void DoIndependentWork()
{
resultsTextBox.Text += "Working . . . . . . .\r\n";
}
}
}
// Sample Output:
// Working . . . . . . .
// Length of the downloaded string: 41564.