次の方法で共有


非同期プログラムの制御フロー (Visual Basic)

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 からダウンロードできます。 次の手順では、プログラムを開いて実行します。

  1. ダウンロードしたファイルを解凍し、Visual Studio を起動します。

  2. メニュー バーで、[ ファイル]、[ 開く]、[ プロジェクト/ソリューション] の順に選択します。

  3. 解凍したサンプル コードを保持するフォルダーに移動し、ソリューション (.sln) ファイルを開き、F5 キーを押してプロジェクトをビルドして実行します。

プログラムを自分でビルドする

次の Windows Presentation Foundation (WPF) プロジェクトには、このトピックのコード例が含まれています。

プロジェクトを実行するには、次の手順を実行します。

  1. Visual Studio を起動します。

  2. メニュー バーで、 [ファイル][新規作成][プロジェクト] の順にクリックします。

    [ 新しいプロジェクト ] ダイアログ ボックスが開きます。

  3. [ インストールされているテンプレート ] ウィンドウで、[ Visual Basic] を選択し、プロジェクトの種類の一覧から [WPF アプリケーション ] を選択します。

  4. プロジェクトの名前として「 AsyncTracer 」と入力し、[ OK] ボタンを選択します。

    ソリューション エクスプローラーに新しいプロジェクトが表示されます。

  5. Visual Studio コード エディターで、 [MainWindow.xaml] タブをクリックします。

    タブが表示されない場合は、 ソリューション エクスプローラーで MainWindow.xaml のショートカット メニューを開き、[ コードの表示] を選択します。

  6. 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 の デザイン ビューに表示されます。

  7. System.Net.Httpの参照を追加します。

  8. ソリューション エクスプローラーで MainWindow.xaml.vb のショートカット メニューを開き、 [コードの表示] を選択します。

  9. 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
    
  10. 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)を呼び出します。 次の図は、メソッドからメソッドへの呼び出しの概要を示しています。

手順 1 と 2 の

AccessTheWebAsyncclient.GetStringAsyncの両方の戻り値の型がTask<TResult>AccessTheWebAsyncの場合、TResult は整数です。 GetStringAsyncの場合、TResult は文字列です。 非同期メソッドの戻り値の型の詳細については、「 非同期の戻り値の型 (Visual Basic)」を参照してください。

タスクを返す非同期メソッドは、コントロールが呼び出し元に戻るときにタスク インスタンスを返します。 呼び出されたメソッドで Await 演算子が見つかったとき、または呼び出されたメソッドが終了したときに、非同期メソッドから呼び出し元に制御が戻ります。 "THREE" から "SIX" のラベルが付いた表示行は、プロセスのこの部分をトレースします。

手順 3

AccessTheWebAsyncでは、ターゲット Web ページの内容をダウンロードするために非同期メソッド GetStringAsync(String)が呼び出されます。 コントロールは、client.GetStringAsyncが返されるときに、client.GetStringAsyncからAccessTheWebAsyncに戻ります。

client.GetStringAsync メソッドは、AccessTheWebAsyncgetStringTask変数に割り当てられている文字列のタスクを返します。 サンプル プログラムの次の行は、 client.GetStringAsync の呼び出しと割り当てを示しています。

Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")

実際の文字列を最終的に生成 client.GetStringAsync することで、タスクを約束と考えることができます。 それまでの間、 AccessTheWebAsyncclient.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 演算子のアプリケーションへの制御フローを示しています。

手順 3

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での通常の処理を示しています。

手順 4

手順 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) が完了した後の制御の転送を示しています。

手順 FIVE

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への制御の戻り値を示しています。

手順 6

こちらも参照ください