Практическое руководство. Параллельное выполнение нескольких веб-запросов с использованием Async и Await (C#)How to make multiple web requests in parallel by using async and await (C#)

В асинхронном методе задачи запускаются в момент создания.In an async method, tasks are started when they’re created. Оператор Await применяется к задаче в той точке в методе, где обработка не может продолжаться до завершения задачи.The await operator is applied to the task at the point in the method where processing can’t continue until the task finishes. Часто задачи ожидаются в момент создания, как показано в следующем примере.Often a task is awaited as soon as it’s created, as the following example shows.

var result = await someWebAccessMethodAsync(url);  

Тем не менее можно отделить создание задачи от ее ожидания, если программа содержит другую работу, выполнение которой не зависит от завершения задачи.However, you can separate creating the task from awaiting the task if your program has other work to accomplish that doesn’t depend on the completion of the task.

// The following line creates and starts the task.  
var myTask = someWebAccessMethodAsync(url);  
  
// While the task is running, you can do other work that doesn't depend  
// on the results of the task.  
// . . . . .  
  
// The application of await suspends the rest of this method until the task is complete.  
var result = await myTask;  

Между моментом запуска задачи и ее ожиданием можно запустить другие задачи.Between starting a task and awaiting it, you can start other tasks. Дополнительные задачи неявно выполняются параллельно, однако дополнительные потоки не создаются.The additional tasks implicitly run in parallel, but no additional threads are created.

Следующая программа запускает три асинхронные веб-загрузки, а затем ожидает их в том порядке, в котором они вызываются.The following program starts three asynchronous web downloads and then awaits them in the order in which they’re called. Обратите внимание, что при запуске программы задачи не всегда завершаются в том порядке, в котором они созданы и ожидаются.Notice, when you run the program, that the tasks don’t always finish in the order in which they’re created and awaited. Они начинают выполняться в момент создания, и одна или несколько задач могут завершиться прежде, чем метод достигает выражения await.They start to run when they’re created, and one or more of the tasks might finish before the method reaches the await expressions.

Примечание

Для выполнения этого проекта необходимо, чтобы на компьютере были установлены Visual Studio 2012 или более поздняя версия и .NET Framework 4.5 или более поздняя версия.To complete this project, you must have Visual Studio 2012 or higher and the .NET Framework 4.5 or higher installed on your computer.

Другой пример, в котором одновременно запускается несколько задач, см. в разделе Практическое руководство. Расширение пошагового руководства по асинхронным процедурам с использованием метода Task.WhenAll (C#).For another example that starts multiple tasks at the same time, see How to extend the async walkthrough by using Task.WhenAll (C#).

Код для этого примера можно скачать на странице Примеры кода от разработчиков.You can download the code for this example from Developer Code Samples.

Настройка проектаTo set up the project

  1. Чтобы настроить приложение WPF, выполните следующие действия.To set up a WPF application, complete the following steps. Подробные инструкции для выполнения этих действий можно найти в разделе Пошаговое руководство. Получение доступа к Интернету с помощью модификатора Async и оператора Await (C#).You can find detailed instructions for these steps in Walkthrough: Accessing the Web by Using async and await (C#).

    • Создайте приложение WPF, которое содержит текстовое поле и кнопку.Create a WPF application that contains a text box and a button. Назовите кнопку startButton, а текстовое поле — resultsTextBox.Name the button startButton, and name the text box resultsTextBox.

    • Добавьте ссылку для System.Net.Http.Add a reference for System.Net.Http.

    • Добавьте в файл MainWindow.xaml.cs директиву using для System.Net.Http.In the MainWindow.xaml.cs file, add a using directive for System.Net.Http.

Добавление кодаTo add the code

  1. В окне конструктора для MainWindow.xaml дважды нажмите кнопку, чтобы создать обработчик события startButton_Click в файле MainWindow.xaml.cs.In the design window, MainWindow.xaml, double-click the button to create the startButton_Click event handler in MainWindow.xaml.cs.

  2. Скопируйте следующий код в тело startButton_Click в файле MainWindow.xaml.cs.Copy the following code, and paste it into the body of startButton_Click in MainWindow.xaml.cs.

    resultsTextBox.Clear();  
    await CreateMultipleTasksAsync();  
    resultsTextBox.Text += "\r\n\r\nControl returned to startButton_Click.\r\n";  
    

    Код вызывает асинхронный метод CreateMultipleTasksAsync, который управляет приложением.The code calls an asynchronous method, CreateMultipleTasksAsync, which drives the application.

  3. Добавьте следующие вспомогательные методы в проект:Add the following support methods to the project:

    • ProcessURLAsync использует метод HttpClient для скачивания содержимого веб-сайта в виде массива байтов.ProcessURLAsync uses an HttpClient method to download the contents of a website as a byte array. Вспомогательный метод ProcessURLAsync затем отображает и возвращает длину массива.The support method, ProcessURLAsync then displays and returns the length of the array.

    • DisplayResults показывает число байтов в массиве байтов для каждого URL-адреса.DisplayResults displays the number of bytes in the byte array for each URL. Эти выходные данные показывают, когда именно каждая задача завершает загрузку.This display shows when each task has finished downloading.

    Скопируйте следующие методы и вставьте их после обработчика событий startButton_Click в файле MainWindow.xaml.cs.Copy the following methods, and paste them after the startButton_Click event handler in MainWindow.xaml.cs.

    async Task<int> ProcessURLAsync(string url, HttpClient client)  
    {  
        var byteArray = await client.GetByteArrayAsync(url);  
        DisplayResults(url, byteArray);  
        return byteArray.Length;  
    }  
    
    private void DisplayResults(string url, byte[] content)  
    {  
        // Display the length of each website. The string format
        // is designed to be used with a monospaced font, such as  
        // Lucida Console or Global Monospace.  
        var bytes = content.Length;  
        // Strip off the "https://".  
        var displayURL = url.Replace("https://", "");  
        resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
    }  
    
  4. Наконец, определите метод CreateMultipleTasksAsync, который выполняет следующие действия.Finally, define method CreateMultipleTasksAsync, which performs the following steps.

    • Этот метод объявляет объект HttpClient, который используется методом доступа GetByteArrayAsync в ProcessURLAsync.The method declares an HttpClient object,which you need to access method GetByteArrayAsync in ProcessURLAsync.

    • Метод создает и запускает три задачи типа Task<TResult>, где TResult является целым числом.The method creates and starts three tasks of type Task<TResult>, where TResult is an integer. При завершении каждой задачи DisplayResults отображает URL-адрес и длину загруженного содержимого задачи.As each task finishes, DisplayResults displays the task's URL and the length of the downloaded contents. Поскольку задачи выполняются асинхронно, порядок, в котором отображаются результаты, может отличаться от порядка, в котором они были объявлены.Because the tasks are running asynchronously, the order in which the results appear might differ from the order in which they were declared.

    • Метод ожидает завершения каждой задачи.The method awaits the completion of each task. Каждый оператор await приостанавливает выполнение CreateMultipleTasksAsync до завершения выполнения ожидаемой задачи.Each await operator suspends execution of CreateMultipleTasksAsync until the awaited task is finished. Оператор также получает возвращаемое значение вызова ProcessURLAsync от каждой завершенной задачи.The operator also retrieves the return value from the call to ProcessURLAsync from each completed task.

    • После завершения задач и получения целочисленных значений метод суммирует длину веб-сайтов и отображает результат.When the tasks have been completed and the integer values have been retrieved, the method sums the lengths of the websites and displays the result.

    Скопируйте следующий метод и вставьте его в решение.Copy the following method, and paste it into your solution.

    private async Task CreateMultipleTasksAsync()  
    {  
        // Declare an HttpClient object, and increase the buffer size. The  
        // default buffer size is 65,536.  
        HttpClient client =  
            new HttpClient() { MaxResponseContentBufferSize = 1000000 };  
    
        // Create and start the tasks. As each task finishes, DisplayResults
        // displays its length.  
        Task<int> download1 =
            ProcessURLAsync("https://msdn.microsoft.com", client);  
        Task<int> download2 =
            ProcessURLAsync("https://msdn.microsoft.com/library/hh156528(VS.110).aspx", client);  
        Task<int> download3 =
            ProcessURLAsync("https://msdn.microsoft.com/library/67w7t67f.aspx", client);  
    
        // Await each task.  
        int length1 = await download1;  
        int length2 = await download2;  
        int length3 = await download3;  
    
        int total = length1 + length2 + length3;  
    
        // Display the total count for the downloaded websites.  
        resultsTextBox.Text += $"\r\n\r\nTotal bytes returned:  {total}\r\n";
    }  
    
  5. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Start .Choose the F5 key to run the program, and then choose the Start button.

    Запустите программу несколько раз, чтобы убедиться, что три задачи не всегда завершаются в том же порядке, а порядок их завершения не обязательно совпадает с порядком, в котором они создаются и ожидаются.Run the program several times to verify that the three tasks don’t always finish in the same order and that the order in which they finish isn't necessarily the order in which they’re created and awaited.

ПримерExample

Следующий код содержит полный пример.The following code contains the full example.

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 the following using directive, and add a reference for System.Net.Http.  
using System.Net.Http;  
  
namespace AsyncExample_MultipleTasks  
{  
    public partial class MainWindow : Window  
    {  
        public MainWindow()  
        {  
            InitializeComponent();  
        }  
  
        private async void startButton_Click(object sender, RoutedEventArgs e)  
        {  
            resultsTextBox.Clear();  
            await CreateMultipleTasksAsync();  
            resultsTextBox.Text += "\r\n\r\nControl returned to startButton_Click.\r\n";  
        }  
  
        private async Task CreateMultipleTasksAsync()  
        {  
            // Declare an HttpClient object, and increase the buffer size. The  
            // default buffer size is 65,536.  
            HttpClient client =  
                new HttpClient() { MaxResponseContentBufferSize = 1000000 };  
  
            // Create and start the tasks. As each task finishes, DisplayResults
            // displays its length.  
            Task<int> download1 =
                ProcessURLAsync("https://msdn.microsoft.com", client);  
            Task<int> download2 =
                ProcessURLAsync("https://msdn.microsoft.com/library/hh156528(VS.110).aspx", client);  
            Task<int> download3 =
                ProcessURLAsync("https://msdn.microsoft.com/library/67w7t67f.aspx", client);  
  
            // Await each task.  
            int length1 = await download1;  
            int length2 = await download2;  
            int length3 = await download3;  
  
            int total = length1 + length2 + length3;  
  
            // Display the total count for the downloaded websites.  
            resultsTextBox.Text += $"\r\n\r\nTotal bytes returned:  {total}\r\n";
        }  
  
        async Task<int> ProcessURLAsync(string url, HttpClient client)  
        {  
            var byteArray = await client.GetByteArrayAsync(url);  
            DisplayResults(url, byteArray);  
            return byteArray.Length;  
        }  
  
        private void DisplayResults(string url, byte[] content)  
        {  
            // Display the length of each website. The string format
            // is designed to be used with a monospaced font, such as  
            // Lucida Console or Global Monospace.  
            var bytes = content.Length;  
            // Strip off the "https://".  
            var displayURL = url.Replace("https://", "");  
            resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
        }
    }  
}  

См. также разделSee also