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


Асинхронное программирование с помощью Async и Await (Visual Basic)

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

Visual Studio 2012 представила упрощенный подход, асинхронное программирование, которое использует асинхронную поддержку в .NET Framework 4.5 и выше, а также в среде выполнения Windows. Компилятор выполняет сложную работу, которую разработчик использовал для выполнения, и приложение сохраняет логическую структуру, которая напоминает синхронный код. В результате вы получаете все преимущества асинхронного программирования с частью усилий.

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

Асинхрон улучшает скорость реагирования

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

В следующей таблице показаны типичные области, в которых асинхронное программирование повышает скорость реагирования. Перечисленные API из .NET Framework 4.5 и среды выполнения Windows содержат методы, поддерживающие асинхронное программирование.

Область приложения Поддержка API, содержащих асинхронные методы
Веб-доступ HttpClient, SyndicationClient
Работа с файлами StorageFile, , StreamWriterStreamReaderXmlReader
Работа с изображениями MediaCapture, , BitmapEncoderBitmapDecoder
Программирование WCF Синхронные и асинхронные операции

Асинхронность является особенно ценной для приложений, которые обращаются к потоку пользовательского интерфейса, так как все UI-активности обычно используют один поток. Если любой процесс заблокирован в синхронном приложении, все блокируются. Приложение перестает отвечать, и вы можете решить, что оно вышло из строя, хотя на самом деле оно просто ожидает.

При использовании асинхронных методов приложение продолжает реагировать на пользовательский интерфейс. Можно изменить размер или свернуть окно, например, или закрыть приложение, если вы не хотите ждать завершения.

Асинхронный подход добавляет эквивалент автоматической передачи в список параметров, которые можно выбрать при разработке асинхронных операций. То есть вы получаете все преимущества традиционного асинхронного программирования, но с гораздо меньшими усилиями от разработчика.

Асинхронные методы проще писать

Ключевые слова Async и Await в Visual Basic являются сердцем асинхронного программирования. Используя эти два ключевых слова, можно использовать ресурсы в .NET Framework или среде выполнения Windows для создания асинхронного метода почти так же легко, как при создании синхронного метода. Асинхронные методы, которые вы определяете с помощью Async и Await, называются асинхронными методами.

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

Полный файл примера Windows Presentation Foundation (WPF) можно найти в конце этого раздела, и вы можете скачать пример из Async Sample: Example from "Асинхронное программирование с помощью Async и Await".

' Three things to note about writing an Async Function:
'  - The function has an Async modifier.
'  - Its return type is Task or Task(Of T). (See "Return Types" section.)
'  - As a matter of convention, its name ends in "Async".
Async Function AccessTheWebAsync() As Task(Of Integer)
    Using client As New HttpClient()
        ' Call and await separately.
        '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
        '  - getStringTask stores the task we get from the call to GetStringAsync.
        '  - Task(Of String) means it is a task which returns a String when it is done.
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com/dotnet")
        ' You can do other work here that doesn't rely on the string from GetStringAsync.
        DoIndependentWork()
        ' The Await operator suspends AccessTheWebAsync.
        '  - AccessTheWebAsync does not continue until getStringTask is complete.
        '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
        '  - Control resumes here when getStringTask is complete.
        '  - The Await operator then retrieves the String result from getStringTask.
        Dim urlContents As String = Await getStringTask
        ' The Return statement specifies an Integer result.
        ' A method which awaits AccessTheWebAsync receives the Length value.
        Return urlContents.Length

    End Using

End Function

Если AccessTheWebAsync у него нет никаких действий, которые он может сделать между вызовом GetStringAsync и ожиданием его завершения, можно упростить код, вызвав и ожидая в следующей одной инструкции.

Dim urlContents As String = Await client.GetStringAsync()

Следующие характеристики обобщают то, что делает предыдущий пример асинхронным методом:

  • Сигнатура Async метода включает модификатор.

  • Имя асинхронного метода по соглашению заканчивается суффиксом Async.

  • Возвращаемый тип является одним из следующих типов:

    • Task(Of TResult), если у метода есть оператор return, в котором операнд имеет тип TResult.
    • Task Если метод не имеет инструкции return или имеет оператор return без операнда.
    • Sub , если вы пишете асинхронный обработчик событий.

    Дополнительные сведения см. в разделе "Возвращаемые типы и параметры" далее в этом разделе.

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

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

Дополнительные сведения об асинхронности в предыдущих версиях .NET Framework см. в статье TPL и традиционное асинхронное программирование .NET Framework.

Что происходит в асинхронном методе

Самое главное, чтобы понять в асинхронном программировании, заключается в том, как поток управления перемещается из метода в метод. Диаграмма ниже проведет вас через процесс:

Схема, показывающая трассировку асинхронной программы.

Числа на схеме соответствуют следующим шагам:

  1. Обработчик событий вызывает и ожидает асинхронный AccessTheWebAsync метод.

  2. AccessTheWebAsync создаёт экземпляр HttpClient и вызывает асинхронный метод GetStringAsync для загрузки содержимого веб-сайта в виде строки.

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

    GetStringAsync возвращает task(Of TResult), где TResult является строкой и AccessTheWebAsync назначает задачу переменной getStringTask . Задача представляет текущий процесс вызова GetStringAsync с обязательством создать фактическое строковое значение, когда работа будет завершена.

  4. Так как getStringTask еще не ожидалось, AccessTheWebAsync может продолжить работу с другими, которые не зависят от окончательного результата GetStringAsync. Эта работа представлена вызовом синхронного метода DoIndependentWork.

  5. DoIndependentWork — синхронный метод, который выполняет свою работу и возвращает управление вызывающему.

  6. AccessTheWebAsync исчерпал работу, которую он может выполнять без получения результата от getStringTask. AccessTheWebAsync далее требуется вычислить и вернуть длину скачаемой строки, но метод не может вычислить это значение, пока метод не будет содержать строку.

    Поэтому AccessTheWebAsync использует оператор await для приостановки выполнения и передачи управления методу, который вызвал AccessTheWebAsync. AccessTheWebAsync возвращает Task(Of Integer) вызывающему. Задача представляет обещание создать целочисленный результат, который является длиной скачаемой строки.

    Замечание

    Если GetStringAsync (и поэтому getStringTask) завершен до того, как AccessTheWebAsync начинает ожидания этого, управление остается в AccessTheWebAsync. Затраты на приостановку и последующее возвращение к AccessTheWebAsync будут напрасны, если вызываемый асинхронный процесс (getStringTask) уже завершен, и AccessTheWebSync не нужно ждать окончательного результата.

    Внутри вызывающего объекта (обработчик событий в этом примере) шаблон обработки продолжается. Вызывающий может выполнить другие действия, не зависящие от результата AccessTheWebAsync, прежде чем ожидать этот результат, или может ожидать немедленно. Обработчик событий ожидает AccessTheWebAsyncи AccessTheWebAsync ожидает GetStringAsync.

  7. GetStringAsync завершает и создает строковый результат. Строковый результат не возвращается вызовом GetStringAsync так, как вы могли бы ожидать. (Помните, что метод уже вернул задачу на шаге 3.) Вместо этого строковый результат хранится в задаче, представляющей завершение метода getStringTask. Оператор await извлекает результат из getStringTask. Инструкция назначения назначает полученный результат urlContents.

  8. При AccessTheWebAsync получении результата строки метод может вычислить длину строки. Затем работа AccessTheWebAsync также завершена, а обработчик события ожидания может возобновить свою работу. В полном примере в конце раздела можно проверить, что обработчик событий извлекает и выводит значение длины результата.

Если вы не знакомы с асинхронным программированием, рассмотрите разницу между синхронным и асинхронным поведением. Синхронный метод возвращается после завершения работы (шаг 5), но асинхронный метод возвращает значение задачи при приостановке работы (шаги 3 и 6). Когда асинхронный метод в конечном итоге завершит свою работу, задача помечается как завершенная, а результат, если он есть, хранится в задаче.

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

Асинхронные методы API

Вы, возможно, задумываетесь, где можно найти такие методы, как GetStringAsync, которые поддерживают асинхронное программирование. Платформа .NET Framework 4.5 или более поздней версии содержит множество элементов, работающих с Async и Await. Эти элементы можно распознать по суффиксу Async, присоединённому к имени элемента, и типу возврата Task или Task(Of TResult). Например, System.IO.Stream класс содержит такие методы, как CopyToAsync, ReadAsyncи WriteAsync наряду с синхронными методами CopyTo, Readи Write.

Среда выполнения Windows также содержит множество методов, которые можно использовать с Async и Await в приложениях Windows. Дополнительные сведения и примеры методов см. в статье Вызов асинхронных API в C# или Visual Basic, асинхронноепрограммирование (приложения среды выполнения Windows) и WhenAny: мост между платформой .NET Framework и средой выполнения Windows.

Потоки

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

Ключевые слова Async и Await не вызывают создания дополнительных потоков. Асинхронные методы не требуют использования многопоточности, так как асинхронный метод не выполняется в своем собственном потоке выполнения. Метод выполняется в текущем контексте синхронизации и использует время в потоке, только если метод активен. Вы можете использовать Task.Run для перемещения работы, привязанной к ЦП, в фоновый поток, но фоновый поток не помогает в процессе, который просто ожидает получения результатов.

Асинхронный подход к асинхронным программированию предпочтительнее существующих подходов практически в каждом случае. В частности, этот подход лучше, чем BackgroundWorker для операций ввода-вывода, так как код проще, и вам не нужно беспокоиться об условиях гонки. В сочетании с Task.Run асинхронное программирование лучше, чем BackgroundWorker для операций, связанных с загрузкой ЦП, так как асинхронное программирование отделяет детали координации выполнения вашего кода от работы, которая Task.Run передается в пул потоков.

Async и Await

Если указать, что метод является асинхронным методом с помощью модификатора Async , включите следующие две возможности.

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

    Приостановка асинхронного метода в Await выражении не представляет собой выход из метода, и Finally блоки не выполняются.

  • Помеченный асинхронный метод может ожидаться методами, вызывающими его.

Асинхронный метод обычно содержит одно или несколько вхождений Await оператора, но отсутствие Await выражений не приводит к ошибке компилятора. Если асинхронный метод не использует Await оператор для маркировки точки приостановки, метод выполняется как синхронный метод, несмотря на Async модификатор. Компилятор выдает предупреждение для таких методов.

Async и Await являются контекстными ключевыми словами. Дополнительные сведения и примеры см. в следующих разделах:

Возвращаемые типы и параметры

В программировании .NET Framework асинхронный метод обычно возвращает Task или задачу (Of TResult). В асинхронном методе оператор Await применяется к задаче, возвращаемой из вызова другого асинхронного метода.

Вы указываете task(Of TResult) в качестве возвращаемого типа, если метод содержит оператор Return , указывающий операнду типа TResult.

Вы используете Task в качестве типа возвращаемого значения, если метод не имеет инструкции return или имеет оператор return, который не возвращает операнду.

В следующем примере показано, как объявить и вызвать метод, возвращающий Task(Of TResult) или Task.

' Signature specifies Task(Of Integer)
Async Function TaskOfTResult_MethodAsync() As Task(Of Integer)

    Dim hours As Integer
    ' . . .
    ' Return statement specifies an integer result.
    Return hours
End Function

' Calls to TaskOfTResult_MethodAsync
Dim returnedTaskTResult As Task(Of Integer) = TaskOfTResult_MethodAsync()
Dim intResult As Integer = Await returnedTaskTResult
' or, in a single statement
Dim intResult As Integer = Await TaskOfTResult_MethodAsync()

' Signature specifies Task
Async Function Task_MethodAsync() As Task

    ' . . .
    ' The method has no return statement.
End Function

' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statement
Await Task_MethodAsync()

Каждая возвращаемая задача представляет текущую работу. Задача инкапсулирует сведения о состоянии асинхронного процесса и в конечном счёте либо конечный результат этого процесса, либо исключение, которое вызывает процесс, если он терпит неудачу.

Асинхронный метод также может быть методом Sub . Этот тип возвращаемого значения используется в основном для определения обработчиков событий, для которых требуется указание типа возвращаемого значения. Асинхронные обработчики событий часто служат отправной точкой для асинхронных программ.

Асинхронная процедура Sub не может выполняться с ожиданием, и вызывающая сторона не может обрабатывать исключения, которые выбрасывает метод.

Асинхронный метод не может объявлять параметры ByRef , но метод может вызывать методы, имеющие такие параметры.

Дополнительные сведения и примеры см. в статье Async Return Types (Visual Basic). Для получения дополнительной информации о том, как обрабатывать исключения в асинхронных методах, см. в разделе Try...Catch...Finally.

Асинхронные API в программировании среды выполнения Windows имеют один из следующих типов возвращаемых значений, аналогичных задачам:

Дополнительные сведения и пример см. в статье "Вызов асинхронных API" в C# или Visual Basic.

Соглашение об именовании

По соглашению к именам методов с модификатором Async добавляется Async.

Вы можете игнорировать соглашение, в котором событие, базовый класс или контракт интерфейса предлагает другое имя. Например, не следует переименовать распространенные обработчики событий, например Button1_Click.

Связанные разделы и примеры (Visual Studio)

Название Описание Образец
Пошаговое руководство. Доступ к Интернету с помощью Async и Await (Visual Basic) Показывает, как преобразовать синхронное решение WPF в асинхронное решение WPF. Приложение скачивает ряд веб-сайтов. Пример асинхронного программирования: асинхронное программирование с помощью Async и Await (Visual Basic)
How to: Extend the Async Walkthrough by Using Task.WhenAll (Visual Basic) (Практическое руководство. Расширение пошагового руководства по асинхронным процедурам с использованием метода Task.WhenAll (Visual Basic)) Добавьте Task.WhenAll в предыдущее пошаговое руководство. Использование WhenAll запускает все загрузки одновременно.
How to: Make Multiple Web Requests in Parallel by Using Async and Await (Visual Basic) (Практическое руководство. Параллельное выполнение нескольких веб-запросов с использованием Async и Await (Visual Basic)) Демонстрирует, как одновременно запускать несколько задач. Асинхронный пример: параллельное выполнение нескольких веб-запросов
Async Return Types (Visual Basic) (Типы возвращаемых значений Async (Visual Basic)) Иллюстрирует типы, которые асинхронные методы могут возвращать и объяснить, когда каждый тип подходит.
Поток управления в асинхронных программах (Visual Basic) Подробно отслеживает, как поток управления проходит через последовательное выполнение выражений ожидания в асинхронной программе. Пример асинхронного кода: поток управления в асинхронных программах
Fine-Tuning Ваше асинхронное приложение (Visual Basic) Показывает, как добавить следующие функции в асинхронное решение:

- Отмена асинхронной задачи или списка задач (Visual Basic)
- Отмена асинхронных задач после периода времени (Visual Basic)
- Отмена оставшихся асинхронных задач после завершения одного (Visual Basic)
- Запуск нескольких асинхронных задач и их обработка по мере их завершения (Visual Basic)
Пример асинхронной настройки приложения
Обработка повторного входа в асинхронных приложениях (Visual Basic) Показывает, как обрабатывать случаи, когда активная асинхронная операция перезапускается в процессе её выполнения.
WhenAny: Мост между .NET Framework и средой выполнения Windows Показывает, как организовать взаимодействие между типами задач в .NET Framework и IAsyncOperations в Windows Runtime, чтобы можно использовать WhenAny с методом Windows Runtime. Пример асинхронного взаимодействия: мост между .NET и средой выполнения Windows (AsTask и WhenAny)
Асинхронная отмена: интеграция между .NET Framework и средой выполнения Windows Показывает, как организовать взаимодействие между типами задач в .NET Framework и IAsyncOperations в Windows Runtime, чтобы можно использовать CancellationTokenSource с методом Windows Runtime. Асинхронный пример: интеграция между .NET и средой выполнения Windows (AsTask и отмена)
Использование async для доступа к файлам (Visual Basic) Перечисляет и демонстрирует преимущества использования async и await для доступа к файлам.
Асинхронный шаблон на основе задач (TAP) Описывает новый шаблон асинхронности в .NET Framework. Шаблон основан на типах Task и типах Task(Of TResult ).

Полный пример

Следующий код — это файл MainWindow.xaml.vb из приложения Windows Presentation Foundation (WPF), который обсуждается в этом разделе. Вы можете скачать пример из раздела 'Async Sample: Пример из "Асинхронное программирование с помощью Async и Await".'


Imports System.Net.Http

' Example that demonstrates Asynchronous Programming with Async and Await.
' It uses HttpClient.GetStringAsync to download the contents of a website.
' Sample Output:
' Working . . . . . . .
'
' Length of the downloaded string: 39678.

Class MainWindow

    ' Mark the event handler with Async so you can use Await in it.
    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

        ' Call and await immediately.
        ' StartButton_Click suspends until AccessTheWebAsync is done.
        Dim contentLength As Integer = Await AccessTheWebAsync()

        ResultsTextBox.Text &= $"{vbCrLf}Length of the downloaded string: {contentLength}.{vbCrLf}"

    End Sub


    ' Three things to note about writing an Async Function:
    '  - The function has an Async modifier.
    '  - Its return type is Task or Task(Of T). (See "Return Types" section.)
    '  - As a matter of convention, its name ends in "Async".
    Async Function AccessTheWebAsync() As Task(Of Integer)

        Using client As New HttpClient()

            ' Call and await separately.
            '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
            '  - getStringTask stores the task we get from the call to GetStringAsync.
            '  - Task(Of String) means it is a task which returns a String when it is done.
            Dim getStringTask As Task(Of String) =
                client.GetStringAsync("https://learn.microsoft.com/dotnet")

            ' You can do other work here that doesn't rely on the string from GetStringAsync.
            DoIndependentWork()

            ' The Await operator suspends AccessTheWebAsync.
            '  - AccessTheWebAsync does not continue until getStringTask is complete.
            '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
            '  - Control resumes here when getStringTask is complete.
            '  - The Await operator then retrieves the String result from getStringTask.
            Dim urlContents As String = Await getStringTask

            ' The Return statement specifies an Integer result.
            ' A method which awaits AccessTheWebAsync receives the Length value.
            Return urlContents.Length

        End Using

    End Function

    Sub DoIndependentWork()
        ResultsTextBox.Text &= $"Working . . . . . . .{vbCrLf}"
    End Sub

End Class

См. также