非同期プログラムにおける制御フロー (Visual Basic)
Async
キーワードと Await
キーワードを使用すると、非同期のプログラムの作成と保守をより簡単に行えます。 ただし、プログラムがどのように動作するかを理解しないと、その結果は予想に反するものになる場合があります。 このトピックでは、簡単な非同期プログラムによる制御フローをトレースして、制御があるメソッドから別のメソッドに移るタイミングと、その都度転送される情報について説明します。
注意
Async
キーワードおよび Await
キーワードは、Visual Studio 2012 で導入されました。
一般に、Async 修飾子を使用した非同期コードを含むメソッドをマークします。 async 修飾子でマークされたメソッドでは、Await (Visual Basic) 演算子を使用して、呼び出される非同期処理の終了をメソッドが待機する場所を指定できます。 詳細については、「Async および Await を使用した非同期プログラミング (Visual Basic)」を参照してください。
次の例では、非同期メソッドを使用して、指定した Web サイトのコンテンツを文字列としてダウンロードし、その文字列の長さを表示します。 この例には、次の 2 つのメソッドが含まれています。
startButton_Click
を呼び出して結果を表示するAccessTheWebAsync
。Web サイトのコンテンツを文字列としてダウンロードして、その文字列の長さを返す
AccessTheWebAsync
。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://video2.skills-academy.com")
' THREE
Dim urlContents As String = Await getStringTask
' FIVE
Return urlContents.Length
End Function
End Class
「1」から「6」までのそれぞれのラベルの位置は、プログラムの現在の状態に関する情報を表示します。 次の出力が生成されます。
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 以降が、コンピューターにインストールされている必要があります。
プログラムをダウンロードする
このトピックのアプリケーションは、「非同期のサンプル:非同期プログラムにおける制御フロー」からダウンロードできます。 次の手順でプログラムを開いて実行します。
ダウンロードしたファイルを解凍し、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://video2.skills-academy.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 キーを押してプログラムを実行し、 [Start] を複数回クリックします。
次の出力が表示されます。
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.
startButton_Click
が AccessTheWebAsync
を呼び出し、AccessTheWebAsync
が非同期 HttpClient メソッド GetStringAsync(String) を呼び出すと、最初の 2 行の表示行がパスをトレースします。 次の図は、メソッドからメソッドへの呼び出しを示しています。
AccessTheWebAsync
と client.GetStringAsync
の戻り値の型はどちらも Task<TResult> です。 AccessTheWebAsync
では、TResult は整数です。 GetStringAsync
では、TResult は文字列です。 非同期メソッドの戻り値の型について詳しくは、「非同期の戻り値の型 (Visual Basic)」を参照してください。
タスクを返す非同期のメソッドは、制御が呼び出し元に戻ると、タスク インスタンスを返します。 Await
演算子が呼び出されたメソッドで実行されるか、または呼び出されたメソッドが終了すると、非同期メソッドから呼び出し元に制御が戻ります。 「3」から「6」のラベルの付いた表示行はこのプロセスの部分をトレースします。
手順 3.
AccessTheWebAsync
で非同期メソッド GetStringAsync(String) が呼び出され、ターゲットの Web ページのコンテンツがダウンロードされます。 client.GetStringAsync
が制御を返すと、AccessTheWebAsync
から client.GetStringAsync
に制御が戻ります。
client.GetStringAsync
メソッドは、getStringTask
の AccessTheWebAsync
変数に割り当てる文字列のタスクを返します。 プログラム例の次の行は、client.GetStringAsync
の呼び出しと割り当てを示しています。
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://video2.skills-academy.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.
次のステートメントは AccessTheWebAsync
が待機中の場合 getStringTask
の進行を中断します。
Dim urlContents As String = Await getStringTask
次の図は client.GetStringAsync
から getStringTask
への割り当てへの制御フロー、および getStringTask
の作成から Await 演算子のアプリケーションへの制御フローを示しています。
await 式は AccessTheWebAsync
が制御を返すまで client.GetStringAsync
を中断します。 その間、コントロールは AccessTheWebAsync
の呼び出し元である startButton_Click
に戻されます。
注意
通常、直ちに非同期メソッドへの呼び出しの待機状態となります。 たとえば、次の割り当てで、getStringTask
を作成してそれを待機する前のコードを置き換えることができます: Dim urlContents As String = Await client.GetStringAsync("https://video2.skills-academy.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.
startButton_Click
が待機すると、getLengthTask
の進行は中断します。 次の代入ステートメントは、startButton_Click
が完了するまで AccessTheWebAsync
を中断します。
Dim contentLength As Integer = Await getLengthTask
次の図で、矢印は AccessTheWebAsync
の await 式から getLengthTask
への値の割り当てへの制御のフロー、および startButton_Click
が待機するまでの getLengthTask
の通常の処理を示しています。
手順 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 式はその値を getLengthTask
の startButton_Click
から取得します。
次の図は、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 式は getLengthTask
から AccessTheWebAsync
の return ステートメントのオペランドである整数値を取得します。 次のステートメントはその値を contentLength
変数に割り当てます。
Dim contentLength As Integer = Await getLengthTask
次の図は AccessTheWebAsync
から startButton_Click
に制御が戻ることを示しています。
関連項目
.NET