Udostępnij za pomocą


Przepływ sterowania w programach asynchronicznych (Visual Basic)

Możesz łatwiej pisać i zarządzać programami asynchronicznymi przy użyciu słów kluczowych Async i Await. Jednak wyniki mogą cię zaskoczyć, jeśli nie rozumiesz sposobu działania programu. W tym temacie śledzi się przepływ sterowania w prostym programie asynchronicznym, aby pokazać, kiedy przepływ sterowania przechodzi z jednej metody do innej i jakie informacje są przesyłane za każdym razem.

Uwaga / Notatka

Słowa kluczowe Async i Await zostały wprowadzone w programie Visual Studio 2012.

Ogólnie rzecz biorąc, metody zawierające kod asynchroniczny są oznaczane modyfikatorem asynchronicznym. W metodzie oznaczonej modyfikatorem asynchronicznym można użyć operatora Await (Visual Basic), aby określić, gdzie metoda wstrzymuje się czekać na ukończenie wywoływanego procesu asynchronicznego. Aby uzyskać więcej informacji, zobacz Asynchroniczne programowanie za pomocą Async i Await (Visual Basic).

W poniższym przykładzie użyto metod asynchronicznych, aby pobrać zawartość określonej witryny internetowej jako ciąg i wyświetlić długość ciągu. Przykład zawiera następujące dwie metody.

  • startButton_Click, który wywołuje AccessTheWebAsync i wyświetla wynik.

  • AccessTheWebAsync, który pobiera zawartość witryny internetowej jako ciąg i zwraca długość ciągu. AccessTheWebAsync używa asynchronicznej metody HttpClient do pobierania zawartości za pomocą GetStringAsync(String).

Ponumerowane linie wyświetlania są wyświetlane w punktach strategicznych w całym programie, aby ułatwić zrozumienie sposobu działania programu i wyjaśnienie, co się dzieje w każdym punkcie oznaczonym. Linie wyświetlania są oznaczone etykietą "ONE" do "SIX". Etykiety reprezentują kolejność, w jakiej program osiąga te linie kodu.

Poniższy kod przedstawia konspekt programu.

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

Każda z oznaczonych etykietami lokalizacji "ONE" do "SIX" wyświetla informacje o bieżącym stanie programu. Generowane są następujące dane wyjściowe:

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.

Konfigurowanie programu

Możesz pobrać kod używany w tym temacie z witryny MSDN lub utworzyć go samodzielnie.

Uwaga / Notatka

Aby uruchomić przykład, na komputerze musi być zainstalowany program Visual Studio 2012 lub nowszy oraz program .NET Framework 4.5 lub nowszy.

Pobieranie programu

Aplikację dla tego tematu można pobrać z async Sample: Control Flow in Async Programs (Przykład asynchroniczny: przepływ sterowania w programach asynchronicznych). Poniższe kroki otwierają i uruchamiają program.

  1. Rozpakuj pobrany plik, a następnie uruchom program Visual Studio.

  2. Na pasku menu wybierz pozycję Plik, Otwórz, Projekt/Rozwiązanie.

  3. Przejdź do folderu zawierającego rozpakowany przykładowy kod, otwórz plik rozwiązania (.sln), a następnie wybierz klucz F5 do skompilowania i uruchomienia projektu.

Samodzielne tworzenie programu

Poniższy projekt Windows Presentation Foundation (WPF) zawiera przykład kodu dla tego tematu.

Aby uruchomić projekt, wykonaj następujące kroki:

  1. Uruchom program Visual Studio.

  2. Na pasku menu wybierz pozycję Plik, Nowy, Projekt.

    Zostanie otwarte okno dialogowe Nowy projekt .

  3. W okienku Zainstalowane szablony wybierz pozycję Visual Basic, a następnie wybierz pozycję Aplikacja WPF z listy typów projektów.

  4. Wprowadź AsyncTracer jako nazwę projektu, a następnie wybierz przycisk OK .

    Nowy projekt zostanie wyświetlony w Eksploratorze rozwiązań.

  5. W edytorze programu Visual Studio Code wybierz kartę MainWindow.xaml .

    Jeśli karta nie jest widoczna, otwórz menu skrótów dla pliku MainWindow.xaml w Eksploratorze rozwiązań, a następnie wybierz pozycję Wyświetl kod.

  6. W widoku XAML pliku MainWindow.xaml zastąp kod następującym kodem.

    <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>
    

    W widoku Projekt MainWindow.xaml pojawi się proste okno zawierające pole tekstowe i przycisk.

  7. Dodaj odwołanie dla elementu System.Net.Http.

  8. W Eksploratorze rozwiązań otwórz menu skrótów dla MainWindow.xaml.vb, a następnie wybierz pozycję Wyświetl kod.

  9. W MainWindow.xaml.vb zastąp kod następującym kodem.

    ' 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. Wybierz F5, aby uruchomić program, a następnie wybierz przycisk Uruchom .

    Powinny zostać wyświetlone następujące dane wyjściowe:

    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.
    

Śledzenie programu

Kroki JEDEN i DWA

Dwa pierwsze wiersze wyświetlania śledzą ścieżkę, gdy startButton_Click wywołuje AccessTheWebAsync, a AccessTheWebAsync wywołuje asynchroniczną metodę HttpClientGetStringAsync(String). Poniższa ilustracja pokazuje wywołania między metodami.

Kroki JEDEN i DWA

Zwracany typ parametru AccessTheWebAsync i client.GetStringAsync ma wartość Task<TResult>. W przypadku funkcji AccessTheWebAsync, TResult jest liczbą całkowitą. W przypadku GetStringAsync, TResult jest ciągiem znaków. Aby uzyskać więcej informacji na temat typów zwracanych metod asynchronicznych, zobacz Async Return Types (Visual Basic).

Metoda asynchroniczna zwracająca obiekt Task zwraca jego instancję, gdy kontrola powraca do obiektu wywołującego. Sterowanie powraca z metody asynchronicznej do obiektu wywołującego, gdy napotkany zostanie operator Await w wywoływanej metodzie lub po zakończeniu wywoływanej metody. Linie wyświetlacza oznaczone etykietami od "TRZY" do "SZEŚĆ" śledzą tę część procesu.

Krok TRZECI

W AccessTheWebAsync wywoływana jest metoda GetStringAsync(String) asynchroniczna w celu pobrania zawartości docelowej strony internetowej. Kontrolka powraca z client.GetStringAsync do AccessTheWebAsync , gdy client.GetStringAsync zwraca.

Metoda client.GetStringAsync zwraca zadanie ciągu przypisanego do zmiennej getStringTask w pliku AccessTheWebAsync. W poniższym wierszu przykładowego programu pokazano wywołanie funkcji client.GetStringAsync oraz przypisanie.

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

Zadanie można traktować jako obietnicę client.GetStringAsync do ostatecznego wytworzenia rzeczywistego ciągu. W międzyczasie, jeśli AccessTheWebAsync ma pracę do wykonania, która nie zależy od obiecanego ciągu z client.GetStringAsync, ta praca może być kontynuowana, podczas gdy client.GetStringAsync oczekuje. W tym przykładzie następujące wiersze danych wyjściowych oznaczone etykietą "TRZY" reprezentują możliwość wykonania niezależnej pracy

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

Poniższa instrukcja zawiesza postęp w AccessTheWebAsync, gdy oczekiwany jest getStringTask.

Dim urlContents As String = Await getStringTask

Na poniższym obrazie pokazano przepływ sterowania z client.GetStringAsync do przypisania do getStringTask i od utworzenia getStringTask do zastosowania operatora Await.

Krok TRZY

Wyrażenie await zawiesza AccessTheWebAsyncclient.GetStringAsync zwróci wartość. W międzyczasie kontrola powraca do obiektu wywołującego AccessTheWebAsync, startButton_Click.

Uwaga / Notatka

Zazwyczaj natychmiast oczekujesz wywołania metody asynchronicznej. Na przykład następujące przypisanie może zastąpić poprzedni kod, który tworzy, a następnie oczekuje na getStringTask: Dim urlContents As String = Await client.GetStringAsync("https://learn.microsoft.com")

W tym temacie operator await zostanie zastosowany później w celu uwzględnienia wierszy wyjściowych, które oznaczają przepływ sterowania za pośrednictwem programu.

Krok CZWARTY

Zadeklarowany zwracany typ to AccessTheWebAsyncTask(Of Integer). W związku z tym, gdy AccessTheWebAsync jest zawieszony, zwraca zadanie liczby całkowitej do startButton_Click. Należy pamiętać, że zwrócone zadanie nie jest getStringTask. Zwrócone zadanie jest nowym zadaniem typu całkowitego, które reprezentuje to, co należy wykonać w zawieszonej metodzie AccessTheWebAsync. Zadanie to obietnica od AccessTheWebAsync na zwrócenie liczby całkowitej, kiedy zadanie zostanie ukończone.

Poniższa instrukcja przypisuje to zadanie do zmiennej getLengthTask .

Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

Podobnie jak w przypadku AccessTheWebAsync, startButton_Click może kontynuować pracę, która nie zależy od wyników zadania asynchronicznego (getLengthTask), dopóki zadanie nie zostanie oczekiwane. Następujące wiersze wyjściowe reprezentują pracę:

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

Postęp w startButton_Click jest zawieszony, gdy oczekiwany jest getLengthTask. Następująca instrukcja przypisania zawiesza startButton_ClickAccessTheWebAsync będzie ukończone.

Dim contentLength As Integer = Await getLengthTask

Na poniższej ilustracji strzałki pokazują przepływ sterowania od wyrażenia await w AccessTheWebAsync do przypisania wartości w getLengthTask, a następnie normalne przetwarzanie w startButton_Click aż do momentu oczekiwania na getLengthTask.

Krok CZTERY

Krok PIĘĆ

Gdy client.GetStringAsync sygnalizuje, że jest ukończone, przetwarzanie w AccessTheWebAsync jest zwalniane z zawieszenia i może kontynuować po instrukcji await. Następujące wiersze danych wyjściowych reprezentują wznowienie przetwarzania:

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

Operand instrukcji return urlContents.Length jest przechowywany w zadaniu, które zwraca AccessTheWebAsync. Wyrażenie await pobiera wartość z getLengthTask w startButton_Click.

Na poniższym obrazku przedstawiono przekazanie kontroli po zakończeniu client.GetStringAsync (i getStringTask).

Krok PIĘĆ

AccessTheWebAsync działa do ukończenia, a kontrola powraca do startButton_Click, który oczekuje na ukończenie.

Krok SZEŚĆ

Gdy AccessTheWebAsync sygnalizuje ukończenie, przetwarzanie może kontynuować po instrukcji await w startButton_Async. W rzeczywistości program nie ma nic więcej do zrobienia.

Następujące wiersze danych wyjściowych reprezentują wznowienie przetwarzania w pliku startButton_Async:

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Wyrażenie await pobiera z getLengthTask wartości całkowitej, która jest operandem instrukcji return w pliku AccessTheWebAsync. Poniższa instrukcja przypisuje tej wartości do zmiennej contentLength .

Dim contentLength As Integer = Await getLengthTask

Na poniższej ilustracji przedstawiono powrót kontrolki z AccessTheWebAsync do startButton_Click.

Krok SZEŚĆ

Zobacz także