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


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

В асинхронном методе задачи запускаются после их создания. Оператор Await (Visual Basic) или await (C#) применяется к задаче в точке метода, где обработка не может продолжаться до тех пор, пока задача не будет выполнена. Часто задача ожидается сразу же после создания, как показано в следующем примере.

Dim result = Await someWebAccessMethodAsync(url)
var result = await someWebAccessMethodAsync(url);

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

' The following line creates and starts the task.
Dim myTask = someWebAccessMethodAsync(url)

' While the task is running, you can do other work that does not depend
' on the results of the task.
' . . . . .

' The application of Await suspends the rest of this method until the task is 
' complete.
Dim result = Await myTask
// 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;

Между запуском задачи и ее ожиданием можно запустить другие задачи. Дополнительные задачи неявно выполняются параллельно, но дополнительные потоки не создаются.

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

Примечание

Для выполнения этого проекта необходимо, чтобы на компьютере была установлена Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 для Windows Desktop, Visual Studio Express 2013 для Windows или .NET Framework 4.5 или 4.5.1.

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

Можно загрузить код этого примера из Примеры кода разработчика.

Настройка проекта

Добавление кода

  1. В окне конструктора MainWindow.xaml дважды щелкните кнопку для создания обработчика событий startButton_Click в MainWindow.xaml.vb или MainWindow.xaml.cs. В качестве альтернативы нажмите кнопку, выберите значок Обработчики событий для выбранных элементов в окне Свойства, а затем введите startButton_Click в текстовом поле Щелчок.

  2. Скопируйте и вставьте следующий код в тело startButton_Click в MainWindow.xaml.vb или MainWindow.xaml.cs.

    resultsTextBox.Clear()
    Await CreateMultipleTasksAsync()
    resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
    
    resultsTextBox.Clear();
    await CreateMultipleTasksAsync();
    resultsTextBox.Text += "\r\n\r\nControl returned to startButton_Click.\r\n";
    

    Код вызывает асинхронный метод CreateMultipleTasksAsync, который управляет приложением.

  3. Добавьте в проект следующие методы поддержки:

    • ProcessURLAsync использует метод HttpClient для загрузки содержимого веб-сайта в виде массива байтов. Дополнительный метод ProcessURLAsync отображает и возвращает длину массива.

    • DisplayResults показывает число байтов в массиве байтов для каждого URL-адреса. Эта информация отображается после завершения загрузки каждой задачей.

    Скопируйте следующие методы и вставьте их после обработчика событий startButton_Click в MainWindow.xaml.vb или MainWindow.xaml.cs.

    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)
    
        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function 
    
    
    Private Sub DisplayResults(url As String, content As Byte())
    
        ' 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. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub
    
    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 "http://".
        var displayURL = url.Replace("http://", "");
        resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
    }
    
  4. Наконец, определите метод CreateMultipleTasksAsync, который выполняет следующие действия.

    • Метод объявляет объект HttpClient, который требуется для доступа к методу GetByteArrayAsync в ProcessURLAsync.

    • Метод создает и запускает три задачи типа Task, где TResult — целое число. При завершении каждой задачи DisplayResults отображает ее URL-адрес и длину загруженного содержимого. Поскольку задачи выполняются асинхронно, порядок, в котором отображаются результаты, может отличаться от порядка, в котором они были объявлены.

    • Метод ожидает завершения каждой задачи. Каждый оператор Await или await приостанавливает выполнение CreateMultipleTasksAsync до завершения ожидаемой задачи. Оператор также получает возвращаемое значение вызова ProcessURLAsync из каждой завершенной задачи.

    • Когда задачи выполнены и целочисленные значения были получены, метод суммирует длину веб-сайтов и отображает результат.

    Скопируйте следующий метод и вставьте его в решение.

    Private Async Function CreateMultipleTasksAsync() As Task
    
        ' Declare an HttpClient object, and increase the buffer size. The 
        ' default buffer size is 65,536. 
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}
    
        ' Create and start the tasks. As each task finishes, DisplayResults  
        ' displays its length. 
        Dim download1 As Task(Of Integer) =
            ProcessURLAsync("https://msdn.microsoft.com", client)
        Dim download2 As Task(Of Integer) =
            ProcessURLAsync("https://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client)
        Dim download3 As Task(Of Integer) =
            ProcessURLAsync("https://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client)
    
        ' Await each task. 
        Dim length1 As Integer = Await download1
        Dim length2 As Integer = Await download2
        Dim length3 As Integer = Await download3
    
        Dim total As Integer = length1 + length2 + length3
    
        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function
    
    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/en-us/library/hh156528(VS.110).aspx", client);
        Task<int> download3 = 
            ProcessURLAsync("https://msdn.microsoft.com/en-us/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 +=
            string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
    }
    
  5. Нажмите клавишу F5, чтобы запустить программу, затем нажмите кнопку Start.

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

Пример

Следующий код содержит полный пример.

' Add the following Imports statements, and add a reference for System.Net.Http. 
Imports System.Net.Http


Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click
        resultsTextBox.Clear()
        Await CreateMultipleTasksAsync()
        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click." 
    End Sub 


    Private Async Function CreateMultipleTasksAsync() As Task

        ' Declare an HttpClient object, and increase the buffer size. The 
        ' default buffer size is 65,536. 
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}

        ' Create and start the tasks. As each task finishes, DisplayResults  
        ' displays its length. 
        Dim download1 As Task(Of Integer) =
            ProcessURLAsync("https://msdn.microsoft.com", client)
        Dim download2 As Task(Of Integer) =
            ProcessURLAsync("https://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client)
        Dim download3 As Task(Of Integer) =
            ProcessURLAsync("https://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client)

        ' Await each task. 
        Dim length1 As Integer = Await download1
        Dim length2 As Integer = Await download2
        Dim length3 As Integer = Await download3

        Dim total As Integer = length1 + length2 + length3

        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function 


    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)

        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function 


    Private Sub DisplayResults(url As String, content As Byte())

        ' 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. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub 
End Class
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/en-us/library/hh156528(VS.110).aspx", client);
            Task<int> download3 = 
                ProcessURLAsync("https://msdn.microsoft.com/en-us/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 +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        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 "http://".
            var displayURL = url.Replace("http://", "");
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
        }
    }
}

См. также

Задачи

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

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

Основные понятия

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