Async
キーワードとAwait
キーワードを使用すると、非同期プログラムをより簡単に記述および保守できます。 ただし、プログラムの動作を理解していない場合は、結果が驚く可能性があります。 このトピックでは、単純な非同期プログラムを介して制御のフローをトレースして、コントロールがメソッド間で移動するタイミングと、毎回転送される情報を示します。
注
Async
キーワードとAwait
キーワードは、Visual Studio 2012 で導入されました。
一般に、非同期コードを含むメソッドを Async 修飾子でマークします。 非同期修飾子でマークされているメソッドでは、 Await (Visual Basic) 演算子を使用して、呼び出された非同期プロセスの完了を待機するためにメソッドが一時停止する場所を指定できます。 詳細については、「 Async と Await を使用した非同期プログラミング (Visual Basic)」を参照してください。
次の例では、非同期メソッドを使用して、指定した Web サイトの内容を文字列としてダウンロードし、文字列の長さを表示します。 この例には、次の 2 つのメソッドが含まれています。
startButton_Click
は、AccessTheWebAsync
を呼び出し、結果を表示します。AccessTheWebAsync
: Web サイトの内容を文字列としてダウンロードし、文字列の長さを返します。AccessTheWebAsync
は、非同期の HttpClient メソッド ( GetStringAsync(String)) を使用してコンテンツをダウンロードします。
プログラムの実行方法を理解し、マークされている各ポイントで何が起こるかを説明するために、プログラム全体の戦略的なポイントに番号付き表示線が表示されます。 表示行には、"ONE" から "SIX" のラベルが付けられます。ラベルは、プログラムがこれらのコード行に到達する順序を表します。
次のコードは、プログラムの概要を示しています。
Class MainWindow
Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click
' ONE
Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
' FOUR
Dim contentLength As Integer = Await getLengthTask
' SIX
ResultsTextBox.Text &=
vbCrLf & $"Length of the downloaded string: {contentLength}." & vbCrLf
End Sub
Async Function AccessTheWebAsync() As Task(Of Integer)
' TWO
Dim client As HttpClient = New HttpClient()
Dim getStringTask As Task(Of String) =
client.GetStringAsync("https://learn.microsoft.com")
' THREE
Dim urlContents As String = Await getStringTask
' FIVE
Return urlContents.Length
End Function
End Class
ラベル付けされた各場所 "ONE" から "SIX" には、プログラムの現在の状態に関する情報が表示されます。 次の出力が生成されます。
ONE: Entering startButton_Click.
Calling AccessTheWebAsync.
TWO: Entering AccessTheWebAsync.
Calling HttpClient.GetStringAsync.
THREE: Back in AccessTheWebAsync.
Task getStringTask is started.
About to await getStringTask & return a Task<int> to startButton_Click.
FOUR: Back in startButton_Click.
Task getLengthTask is started.
About to await getLengthTask -- no caller to return to.
FIVE: Back in AccessTheWebAsync.
Task getStringTask is complete.
Processing the return statement.
Exiting from AccessTheWebAsync.
SIX: Back in startButton_Click.
Task getLengthTask is finished.
Result from AccessTheWebAsync is stored in contentLength.
About to display contentLength and exit.
Length of the downloaded string: 33946.
プログラムを設定する
このトピックで使用するコードは MSDN からダウンロードすることも、自分でビルドすることもできます。
注
この例を実行するには、Visual Studio 2012 以降と .NET Framework 4.5 以降がコンピューターにインストールされている必要があります。
プログラムをダウンロードする
このトピックのアプリケーションは 、Async Sample: Control Flow in Async Programs からダウンロードできます。 次の手順では、プログラムを開いて実行します。
ダウンロードしたファイルを解凍し、Visual Studio を起動します。
メニュー バーで、[ ファイル]、[ 開く]、[ プロジェクト/ソリューション] の順に選択します。
解凍したサンプル コードを保持するフォルダーに移動し、ソリューション (.sln) ファイルを開き、F5 キーを押してプロジェクトをビルドして実行します。
プログラムを自分でビルドする
次の Windows Presentation Foundation (WPF) プロジェクトには、このトピックのコード例が含まれています。
プロジェクトを実行するには、次の手順を実行します。
Visual Studio を起動します。
メニュー バーで、 [ファイル] 、 [新規作成] 、 [プロジェクト] の順にクリックします。
[ 新しいプロジェクト ] ダイアログ ボックスが開きます。
[ インストールされているテンプレート ] ウィンドウで、[ Visual Basic] を選択し、プロジェクトの種類の一覧から [WPF アプリケーション ] を選択します。
プロジェクトの名前として「
AsyncTracer
」と入力し、[ OK] ボタンを選択します。ソリューション エクスプローラーに新しいプロジェクトが表示されます。
Visual Studio コード エディターで、 [MainWindow.xaml] タブをクリックします。
タブが表示されない場合は、 ソリューション エクスプローラーで MainWindow.xaml のショートカット メニューを開き、[ コードの表示] を選択します。
MainWindow.xaml の XAML ビューで、コードを次のコードに置き換えます。
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow" Title="Control Flow Trace" Height="350" Width="525"> <Grid> <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="221,10,0,0" VerticalAlignment="Top" Width="75"/> <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="510" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" d:LayoutOverrides="HorizontalMargin"/> </Grid> </Window>
テキスト ボックスとボタンを含む単純なウィンドウが、MainWindow.xaml の デザイン ビューに表示されます。
System.Net.Httpの参照を追加します。
ソリューション エクスプローラーで MainWindow.xaml.vb のショートカット メニューを開き、 [コードの表示] を選択します。
MainWindow.xaml.vbで、コードを次のコードに置き換えます。
' Add an Imports statement and a reference for System.Net.Http. Imports System.Net.Http Class MainWindow Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click ' The display lines in the example lead you through the control shifts. ResultsTextBox.Text &= "ONE: Entering StartButton_Click." & vbCrLf & " Calling AccessTheWebAsync." & vbCrLf Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync() ResultsTextBox.Text &= vbCrLf & "FOUR: Back in StartButton_Click." & vbCrLf & " Task getLengthTask is started." & vbCrLf & " About to await getLengthTask -- no caller to return to." & vbCrLf Dim contentLength As Integer = Await getLengthTask ResultsTextBox.Text &= vbCrLf & "SIX: Back in StartButton_Click." & vbCrLf & " Task getLengthTask is finished." & vbCrLf & " Result from AccessTheWebAsync is stored in contentLength." & vbCrLf & " About to display contentLength and exit." & vbCrLf ResultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength) End Sub Async Function AccessTheWebAsync() As Task(Of Integer) ResultsTextBox.Text &= vbCrLf & "TWO: Entering AccessTheWebAsync." ' Declare an HttpClient object. Dim client As HttpClient = New HttpClient() ResultsTextBox.Text &= vbCrLf & " Calling HttpClient.GetStringAsync." & vbCrLf ' GetStringAsync returns a Task(Of String). Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com") ResultsTextBox.Text &= vbCrLf & "THREE: Back in AccessTheWebAsync." & vbCrLf & " Task getStringTask is started." ' AccessTheWebAsync can continue to work until getStringTask is awaited. ResultsTextBox.Text &= vbCrLf & " About to await getStringTask & return a Task(Of Integer) to StartButton_Click." & vbCrLf ' Retrieve the website contents when task is complete. Dim urlContents As String = Await getStringTask ResultsTextBox.Text &= vbCrLf & "FIVE: Back in AccessTheWebAsync." & vbCrLf & " Task getStringTask is complete." & vbCrLf & " Processing the return statement." & vbCrLf & " Exiting from AccessTheWebAsync." & vbCrLf Return urlContents.Length End Function End Class
F5 キーを押してプログラムを実行し、[ スタート ] ボタンを選択します。
次の出力が表示されます。
ONE: Entering startButton_Click. Calling AccessTheWebAsync. TWO: Entering AccessTheWebAsync. Calling HttpClient.GetStringAsync. THREE: Back in AccessTheWebAsync. Task getStringTask is started. About to await getStringTask & return a Task<int> to startButton_Click. FOUR: Back in startButton_Click. Task getLengthTask is started. About to await getLengthTask -- no caller to return to. FIVE: Back in AccessTheWebAsync. Task getStringTask is complete. Processing the return statement. Exiting from AccessTheWebAsync. SIX: Back in startButton_Click. Task getLengthTask is finished. Result from AccessTheWebAsync is stored in contentLength. About to display contentLength and exit. Length of the downloaded string: 33946.
プログラムのトレース
手順 1 と 2
最初の 2 つの表示行は、AccessTheWebAsync
を呼び出すstartButton_Click
パスをトレースし、AccessTheWebAsync
非同期HttpClient メソッドGetStringAsync(String)を呼び出します。 次の図は、メソッドからメソッドへの呼び出しの概要を示しています。
AccessTheWebAsync
とclient.GetStringAsync
の両方の戻り値の型がTask<TResult>。
AccessTheWebAsync
の場合、TResult は整数です。
GetStringAsync
の場合、TResult は文字列です。 非同期メソッドの戻り値の型の詳細については、「 非同期の戻り値の型 (Visual Basic)」を参照してください。
タスクを返す非同期メソッドは、コントロールが呼び出し元に戻るときにタスク インスタンスを返します。 呼び出されたメソッドで Await
演算子が見つかったとき、または呼び出されたメソッドが終了したときに、非同期メソッドから呼び出し元に制御が戻ります。 "THREE" から "SIX" のラベルが付いた表示行は、プロセスのこの部分をトレースします。
手順 3
AccessTheWebAsync
では、ターゲット Web ページの内容をダウンロードするために非同期メソッド GetStringAsync(String)が呼び出されます。 コントロールは、client.GetStringAsync
が返されるときに、client.GetStringAsync
からAccessTheWebAsync
に戻ります。
client.GetStringAsync
メソッドは、AccessTheWebAsync
のgetStringTask
変数に割り当てられている文字列のタスクを返します。 サンプル プログラムの次の行は、 client.GetStringAsync
の呼び出しと割り当てを示しています。
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")
実際の文字列を最終的に生成 client.GetStringAsync
することで、タスクを約束と考えることができます。 それまでの間、 AccessTheWebAsync
が client.GetStringAsync
の約束された文字列に依存しない作業を行う場合、その作業は client.GetStringAsync
待機中に続行できます。 この例では、"THREE" というラベルが付いた次の出力行は、独立した作業を行う機会を表しています。
THREE: Back in AccessTheWebAsync.
Task getStringTask is started.
About to await getStringTask & return a Task<int> to startButton_Click.
次のステートメントは、getStringTask
が待機しているときに、AccessTheWebAsync
の進行状況を中断します。
Dim urlContents As String = Await getStringTask
次の図は、 client.GetStringAsync
から getStringTask
への割り当て、および getStringTask
の作成から Await 演算子のアプリケーションへの制御フローを示しています。
await 式は、client.GetStringAsync
が返されるまでAccessTheWebAsync
を中断します。 その間、コントロールは AccessTheWebAsync
の呼び出し元に戻 startButton_Click
。
注
通常は、非同期メソッドの呼び出しをすぐに待機します。 たとえば、次の割り当ては、 getStringTask
を作成して待機する前のコードを置き換えることができます。 Dim urlContents As String = Await client.GetStringAsync("https://learn.microsoft.com")
このトピックでは、プログラムを介した制御フローを示す出力行に対応するために、await 演算子を後で適用します。
手順 4
AccessTheWebAsync
の宣言された戻り値の型がTask(Of Integer)
。 したがって、 AccessTheWebAsync
が中断されると、整数のタスクが startButton_Click
に返されます。 返されたタスクが getStringTask
されていないことを理解しておく必要があります。 返されるタスクは、中断されたメソッド ( AccessTheWebAsync
) で実行する残りの処理を表す整数の新しいタスクです。 タスクは、タスクが完了したときに整数を生成する AccessTheWebAsync
からの約束です。
次のステートメントでは、このタスクを getLengthTask
変数に割り当てます。
Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
AccessTheWebAsync
と同様に、startButton_Click
は、タスクが待機されるまで、非同期タスク (getLengthTask
) の結果に依存しない作業を続行できます。 次の出力行はその動作を表しています。
FOUR: Back in startButton_Click.
Task getLengthTask is started.
About to await getLengthTask -- no caller to return to.
getLengthTask
が待機すると、startButton_Click
の進行状況が中断されます。 次の代入ステートメントは、AccessTheWebAsync
が完了するまでstartButton_Click
を中断します。
Dim contentLength As Integer = Await getLengthTask
次の図では、矢印は、AccessTheWebAsync
の await 式からgetLengthTask
への値の割り当てまでの制御フローを示し、その後、getLengthTask
が待機されるまでのstartButton_Click
での通常の処理を示しています。
手順 5
client.GetStringAsync
が完了したことを通知すると、AccessTheWebAsync
での処理は中断から解放され、await ステートメントを超えて続行できます。 次の出力行は、処理の再開を表します。
FIVE: Back in AccessTheWebAsync.
Task getStringTask is complete.
Processing the return statement.
Exiting from AccessTheWebAsync.
return ステートメントのオペランド ( urlContents.Length
) は、 AccessTheWebAsync
が返すタスクに格納されます。 await 式は、startButton_Click
内のgetLengthTask
からその値を取得します。
次の図は、 client.GetStringAsync
(および getStringTask
) が完了した後の制御の転送を示しています。
AccessTheWebAsync
は完了まで実行され、制御は完了を待機している startButton_Click
に戻ります。
手順 6
AccessTheWebAsync
が完了したことを通知すると、startButton_Async
の await ステートメントを超えて処理を続行できます。 実際、このプログラムには他に何もする必要はありません。
次の出力行は、 startButton_Async
での処理の再開を表します。
SIX: Back in startButton_Click.
Task getLengthTask is finished.
Result from AccessTheWebAsync is stored in contentLength.
About to display contentLength and exit.
await 式は、AccessTheWebAsync
の return ステートメントのオペランドである整数値getLengthTask
から取得します。 次のステートメントでは、その値を contentLength
変数に割り当てます。
Dim contentLength As Integer = Await getLengthTask
次の図は、 AccessTheWebAsync
から startButton_Click
への制御の戻り値を示しています。
こちらも参照ください
.NET