Чтобы прочитать статью на английском языке, установите флажок Английский. Вы также можете просматривать текст на английском языке во всплывающем окне, наводя указатель мыши на текст.
Перевод
Английский
Рекомендуем использовать Visual Studio 2017

Асинхронное программирование с использованием ключевых слов Async и Await (C# и Visual Basic)

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

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

В этом разделе содержатся следующие подразделы.

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

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

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

Область приложения

Поддерживающие интерфейсы API, которые содержат асинхронные методы

Веб-доступ

HttpClient, SyndicationClient

Работа с файлами

StorageFile StreamWriter, StreamReader, XmlReader

Работа с образами

MediaCapture, BitmapEncoderBitmapDecoder

Программирование с использованием WCF

Синхронные и асинхронные операции

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

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

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

Ключевые слова Async и Await в Visual Basic и ключевые слова async и await в C# являются основой асинхронного программирования. Они позволяют использовать ресурсы платформы .NET Framework или Среда выполнения Windows для создания асинхронных методов, и это почти так же просто, как создавать синхронные методы. Асинхронные методы, которые определяются с помощью ключевых слов async и await, называются методами async.

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

Полный файл примера представлен в конце раздела. Также его можно загрузить из статьи Пример асинхронности. Пример из руководства "Асинхронное программирование с помощью Async и Await".

// Three things to note in the signature: 
//  - The method has an async modifier.  
//  - The return type is Task or Task<T>. (See "Return Types" section.)
//    Here, it is Task<int> because the return statement returns an integer. 
//  - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the 
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

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

    // The await operator suspends AccessTheWebAsync. 
    //  - AccessTheWebAsync can't 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. 
    string urlContents = await getStringTask;

    // The return statement specifies an integer result. 
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
    return urlContents.Length;
}

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

string urlContents = await client.GetStringAsync();

Далее поясняется, почему код предыдущего примера является асинхронным методом.

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

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

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

    • Task, если метод имеет оператор Return с операндом типа TResult.

    • Task, если метод не имеет оператора Return или имеет оператор Return без операнда.

    • Void (Sub в Visual Basic), если создается асинхронный обработчик событий.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Примечание Примечание

    Если метод 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). Когда асинхронный метод в конечном счете завершает работу, задача помечается как завершенная и результат, при его наличии, сохраняется в задаче.

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

Где же найти методы для асинхронного программирования (такие как GetStringAsync)? .NET Framework 4,5 имеет множество членов, поддерживающих async и await. Они содержат суффикс Async в своем имени и возвращают тип Task или Task. Например, класс System.IO.Stream имеет методы CopyToAsync, ReadAsync и WriteAsync наряду с синхронными методами CopyTo, Read и Write.

Среда выполнения Windows также содержит множество методов, которые можно использовать с операторами async и await в приложениях Магазин Windows. Дополнительные сведения и примеры методов см. в разделах Краткое руководство: вызов асинхронных API в C# и Visual Basic, Асинхронное программирование (приложения Магазина Windows) и WhenAny. Связывание .NET Framework и среды выполнения Windows (C# и Visual Basic).

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

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

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

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

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

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

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

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

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

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

В качестве возвращаемого типа указывается Task, если метод содержит оператор Return (Visual Basic) или return (C#), который задает операнд типа TResult.

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

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

// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();


// Signature specifies Task
async Task Task_MethodAsync()
{
    // . . .
    // The method has no return statement.  
}

// Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
// or, in a single statement
await Task_MethodAsync();

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

Асинхронный метод может также быть методом Sub (Visual Basic) или иметь тип возвращаемого значения void (C#). Возвращаемый тип в основном используется для определения обработчиков событий, где требуется возвращать тип void. Асинхронные обработчики событий часто служат в качестве отправной точки для асинхронных программ.

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

Асинхронный метод не может объявлять параметры ByRef в Visual Basic и ref или out в C#, но может вызывать методы с такими параметрами.

Дополнительные сведения и примеры см. в разделе Асинхронные типы возвращаемых значений (C# и Visual Basic). Дополнительные сведения о перехвате исключений в асинхронных методах см. в разделе try-catch (Справочник по C#) или Оператор Try... Catch... Finally (Visual Basic).

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

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

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

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

Название

Описание

Пример

Пошаговое руководство. Получение доступа к Интернету с помощью модификатора Async и оператора Await (C# и Visual Basic)

Иллюстрирует преобразование синхронного решения WPF в асинхронное. Приложение загружает ряд веб-сайтов.

Пример асинхронности. Пошаговое руководство "Доступ к сети" (C# и Visual Basic)

Практическое руководство. Расширение пошагового руководства по асинхронным процедурам с использованием метода Task.WhenAll (C# и Visual Basic)

Добавляет Task.WhenAll к предыдущему пошаговому руководству. Использование WhenAll запускает все загрузки одновременно.

Практическое руководство. Параллельное выполнение нескольких веб-запросов с использованием Async и Await (C# и Visual Basic)

Иллюстрирует, как запустить несколько задач одновременно.

Пример асинхронности. Параллельное выполнение нескольких веб-запросов (C# и Visual Basic)

Асинхронные типы возвращаемых значений (C# и Visual Basic)

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

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

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

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

Настройка асинхронного приложения (C# и Visual Basic)

Иллюстрирует добавление следующих функциональных возможностей в асинхронное решение:

Пример асинхронности. Точная настройка приложения (C# и Visual Basic)

Обработка повторного входа в асинхронных приложениях (C# и Visual Basic)

Иллюстрирует обработку случаев, в которых активная асинхронная операция перезапускается при выполнении.

WhenAny. Связывание .NET Framework и среды выполнения Windows (C# и Visual Basic)

Иллюстрирует, как создать мост между типами задач в платформе .NET Framework и IAsyncOperations в Среда выполнения Windows, чтобы можно было использовать метод Среда выполнения Windows с методом WhenAny``1.

Пример асинхронности. Создание моста между .NET и средой выполнения Windows (AsTask и WhenAny)

Асинхронная отмена. Связывание .NET Framework и среды выполнения Windows (C# и Visual Basic)

Иллюстрирует, как создать мост между типами задач в платформе .NET Framework и IAsyncOperations в Среда выполнения Windows, чтобы можно было использовать метод CancellationTokenSource с методом Среда выполнения Windows.

Пример асинхронности. Создание моста между .NET и средой выполнения Windows (AsTask и отмена)

Использование метода Async для доступа к файлам (C# и Visual Basic)

Иллюстрирует преимущества использования асинхронности и ожидания для доступа к файлам.

Пошаговое руководство. Использование отладчика с асинхронными методами

Иллюстрирует поток управления в операторе await и поведение команд Шаг с заходом, Шаг с обходом и Шаг с выходом в пределах асинхронных методов.

Асинхронный шаблон, основанный на задачах (TAP)

Описывает новый шаблон для асинхронности в платформе .NET Framework. Шаблон основан на типах Task и Task.

Краткое руководство: вызов асинхронных API в C# и Visual Basic

Иллюстрирует использование async и await в приложении Магазин Windows.

Асинхронное программирование (приложения среды выполнения Windows)

Предоставляет общие сведения об асинхронном программировании в Среда выполнения Windows.

Видеоролики об Async на Channel 9

Предоставляет ссылки на различные видеоролики об асинхронном программировании.

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

// Add a using directive and a reference for System.Net.Http; 
using System.Net.Http;

namespace AsyncFirstExample
{
    public partial class MainWindow : Window
    {
        // Mark the event handler with async so you can use await in it. 
        private async void StartButton_Click(object sender, RoutedEventArgs e)
        {
            // Call and await separately. 
            //Task<int> getLengthTask = AccessTheWebAsync(); 
            //// You can do independent work here. 
            //int contentLength = await getLengthTask; 

            int contentLength = await AccessTheWebAsync();

            resultsTextBox.Text +=
                String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
        }


        // Three things to note in the signature: 
        //  - The method has an async modifier.  
        //  - The return type is Task or Task<T>. (See "Return Types" section.)
        //    Here, it is Task<int> because the return statement returns an integer. 
        //  - The method name ends in "Async."
        async Task<int> AccessTheWebAsync()
        { 
            // You need to add a reference to System.Net.Http to declare client.
            HttpClient client = new HttpClient();

            // GetStringAsync returns a Task<string>. That means that when you await the 
            // task you'll get a string (urlContents).
            Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

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

            // The await operator suspends AccessTheWebAsync. 
            //  - AccessTheWebAsync can't 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. 
            string urlContents = await getStringTask;

            // The return statement specifies an integer result. 
            // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
            return urlContents.Length;
        }


        void DoIndependentWork()
        {
            resultsTextBox.Text += "Working . . . . . . .\r\n";
        }
    }
}

// Sample Output: 

// Working . . . . . . . 

// Length of the downloaded string: 41564.
Показ: