Поделиться через


Поток управления в асинхронных программах (Visual Basic)

С помощью ключевых слов Async и Await можно легче создавать и поддерживать асинхронные программы. Однако результаты могут удивить вас, если вы не понимаете, как работает ваша программа. В этом разделе отслеживается поток управления с помощью простой асинхронной программы, чтобы показать, когда элемент управления перемещается из одного метода в другой и какие сведения передаются каждый раз.

Замечание

Ключевые слова Async и Await были введены в Visual Studio 2012.

Как правило, вы помечаете методы, содержащие асинхронный код с помощью модификатора Async . В методе, помеченном асинхронным модификатором, можно использовать оператор Await (Visual Basic), чтобы указать, где метод приостанавливает ожидание завершения вызываемого асинхронного процесса. Дополнительные сведения см. в статье асинхронное программирование с помощью Async и Await (Visual Basic).

В следующем примере используются асинхронные методы для скачивания содержимого указанного веб-сайта в виде строки и отображения длины строки. Пример содержит следующие два метода.

  • startButton_Click, который вызывает AccessTheWebAsync и отображает результат.

  • AccessTheWebAsync, который скачивает содержимое веб-сайта в виде строки и возвращает длину строки. AccessTheWebAsync использует асинхронный HttpClient метод, GetStringAsync(String)чтобы скачать содержимое.

Нумерованные линии отображения отображаются в стратегических точках по всей программе, чтобы понять, как выполняется программа, и объяснить, что происходит в каждой точке, помеченной. Отображаемые строки обозначены как "ONE" по "ШЕСТЬ". Метки представляют порядок, в котором программа достигает этих строк кода.

В следующем коде показана структура программы.

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: управление потоком в асинхронных программах. Эти шаги открывают и запускают программу.

  1. Распакуйте скачанный файл и запустите Visual Studio.

  2. В строке меню выберите "Файл", "Открыть", "Проект или решение".

  3. Перейдите в папку с распакованным примером кода, откройте файл решения (.sln), а затем выберите ключ F5 для сборки и запуска проекта.

Создание программы самостоятельно

Следующий проект Windows Presentation Foundation (WPF) содержит пример кода для этого раздела.

Чтобы запустить проект, выполните следующие действия.

  1. Запустите Visual Studio.

  2. В строке меню выберите "Файл", "Создать", "Проект".

    Откроется диалоговое окно "Новый проект ".

  3. В области установленных шаблонов выберите Visual Basic и выберите приложение WPF из списка типов проектов.

  4. Введите AsyncTracer имя проекта и нажмите кнопку "ОК ".

    Новый проект появится в обозревателе решений.

  5. В редакторе Visual Studio Code выберите вкладку MainWindow.xaml .

    Если вкладка не отображается, откройте контекстное меню mainWindow.xaml в обозревателе решений и выберите команду View Code.

  6. В представлении XAML MainWindow.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, чтобы запустить программу, а затем нажмите кнопку 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.
    

Трассировка программы

Шаги ОДИН и ДВА

Первые две строки экрана трассируют путь, где startButton_Click вызывает AccessTheWebAsync, а AccessTheWebAsync вызывает асинхронный метод HttpClientGetStringAsync(String). На следующем рисунке показаны переходы от одного метода к другому.

Шаги ОДИН и ДВА

Возвращаемый тип AccessTheWebAsync и client.GetStringAsyncTask<TResult>. Для AccessTheWebAsync, TResult является целым числом. Для GetStringAsync, TResult является строкой. Дополнительные сведения о типах возврата асинхронного метода см. в статье Async Return Types (Visual Basic).

Асинхронный метод, возвращающий задачу, возвращает экземпляр задачи, когда управление возвращается вызывающему объекту. Управление возвращается из асинхронного метода к вызывающей стороне либо при обнаружении оператора Await в вызываемом методе, либо при завершении вызываемого метода. Отображаемые строки, помеченные как "ТРИ" и "ШЕСТЬ", трассируют эту часть процесса.

Шаг ТРИ

В AccessTheWebAsync вызывается асинхронный метод GetStringAsync(String) для загрузки содержимого целевой веб-страницы. Управление возвращается от client.GetStringAsync к AccessTheWebAsync, когда происходит возврат из client.GetStringAsync.

Метод client.GetStringAsync возвращает задачу типа string, которая назначена переменной getStringTask в AccessTheWebAsync. В следующей строке в примере программы показан вызов client.GetStringAsync и назначение.

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

Вы можете рассматривать задачу как обещание со стороны client.GetStringAsync в конце концов создать фактическую строку. В то же время, если у AccessTheWebAsync есть работа, которая не зависит от обещанной строки от client.GetStringAsync, эта работа может продолжаться, пока client.GetStringAsync ожидает. В примере строки выходных данных, обозначенные как "ТРИ", представляют собой возможность для выполнения самостоятельной работы.

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://learn.microsoft.com")

В этом разделе оператор await применяется позже, чтобы учитывать выходные строки, которые обозначают поток управления через программу.

Шаг ЧЕТЫРЕ

Объявленный тип возвращаемого значения AccessTheWebAsyncTask(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

На следующем рисунке стрелки показывают поток управления от выражения await в AccessTheWebAsync к назначению значения getLengthTask, а затем обычная обработка в startButton_Click продолжается, пока getLengthTask не будет ожидаемым.

Шаг Четыре

Шаг ПЯТЬ

Когда 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, который находится в ожидании завершения.

Шаг ШЕСТЬ

Когда AccessTheWebAsync сигнализирует о завершении, обработка может продолжиться после инструкции await в startButton_Async. На самом деле, программе больше ничего не нужно делать.

Следующие строки выходных данных представляют возобновление обработки в 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, которое является операндом инструкции return в AccessTheWebAsync. Следующая инструкция назначает это значение переменной contentLength .

Dim contentLength As Integer = Await getLengthTask

На изображении ниже показана передача контроля от AccessTheWebAsync к startButton_Click.

Шаг ШЕСТЬ

См. также