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


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

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

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

Это пошаговое руководство начинается с синхронного приложения Windows Presentation Foundation (WPF), которое суммирует количество байтов в списке веб-сайтов. Затем пошаговое руководство преобразует приложение в асинхронное решение с помощью новых функций.

Если вручную не требуется построение приложения можно загрузить пример: "Async Доступ к пошаговом руководстве Интернета (C-# и Visual Basic) из Примеры кода разработчика".

В этом пошаговом руководстве выполняются следующие задачи:

  • Create a WPF application.

  • Design a simple WPF MainWindow window.

  • Add a reference.

  • Add Imports statements or using directives.

  • Create a synchronous solution.

  • Test the synchronous solution.

  • Convert GetURLContents to an asynchronous method.

  • Convert SumPageSizes to an asynchronous method.

  • Convert startButton_Click to an asynchronous method.

  • Test the asynchronous solution.

  • Replace GetURLContentsAsync with a .NET Framework method.

  • Complete Code Examples from the Walkthrough

Обязательные компоненты

На компьютере должна быть установлена среда Visual Studio 2012. Дополнительные сведения см. на веб-сайте Microsoft.

Создание приложения WPF

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

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

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

  3. На панели Установленные шаблоны выберите Visual Basic или Visual C#, а затем выберите Приложение WPF из списка типов проектов.

  4. В текстовом поле Имя введите AsyncExampleWPF, а затем нажмите кнопку ОК.

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

Разработка простого окна WPF MainWindow

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

  2. Если окно Панель элементов не отображается, откройте меню Вид, а затем выберите Панель элементов.

  3. Добавьте элемент управления Button и TextBox в окно MainWindow.

  4. Выделите элемент управления TextBox и в окне Свойства установите следующие значения:

    • Установите свойству Name значение resultsTextBox.

    • Задайте свойству Height значение 250.

    • Задайте свойству Width значение 500.

    • На вкладке Текст укажите моноширинный шрифт, например, Lucida Console или Global Monospace.

  5. Выделите элемент управления Button и в окне Свойства установите следующие значения:

    • Задайте свойству Name значение startButton.

    • Измените значение свойства Content с Button на Start.

  6. Разместите текстовое поле и кнопку так, чтобы оба отображались в окне MainWindow.

    Дополнительные сведения о конструкторе WPF XAML см. в разделе Создание пользовательского интерфейса с помощью конструктора XAML.

Добавление ссылки

  1. В Обозревателя решений выделите имя вашего проекта.

  2. В строке меню выберите Проект, Добавить ссылку.

    Появится диалоговое окно Диспетчер ссылок.

  3. Вверху диалогового окна убедитесь, что ваш проект предназначен для .NET Framework 4.5.

  4. В области Сборки выберите Framework, если она еще не выбрана.

  5. В списке имен установите флажок System.Net.Http.

  6. Выберите кнопку ОК чтобы закрыть диалоговое окно.

Добавление необходимых операторов Imports или директив using

  1. В Обозревателе решений откройте контекстное меню для MainWindow.xaml.vb или MainWindow.xaml.cs, а затем выберите Код.

  2. В верхнюю часть файла с кодом добавьте следующие операторы Imports (в Visual Basic) или директивы using (в C#), если это еще не сделано.

    Imports System.Net.Http
    Imports System.Net
    Imports System.IO
    
    using System.Net.Http;
    using System.Net;
    using System.IO;
    

Создание синхронного приложения

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

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

    resultsTextBox.Clear()
    SumPageSizes()
    resultsTextBox.Text &= vbCrLf & "Control returned to startButton_Click."
    
    resultsTextBox.Clear();
    SumPageSizes();
    resultsTextBox.Text += "\r\nControl returned to startButton_Click.";
    

    Код вызывает метод, который управляет приложением, SumPageSizes, и отображает сообщение, когда управление возвращается к startButton_Click.

  3. Код для синхронного решения содержит следующие четыре метода:

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

    • SetUpURLList, который создает и возвращает список веб-адресов.

    • GetURLContents, который загружает содержимое каждого веб-сайта и возвращает содержимое в виде массива байтов.

    • DisplayResults, который показывает число байтов в массиве байтов для каждого URL-адреса.

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

    Private Sub SumPageSizes()
    
        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()
    
        Dim total = 0
        For Each url In urlList
            ' GetURLContents returns the contents of url as a byte array. 
            Dim urlContents As Byte() = GetURLContents(url)
    
            DisplayResults(url, urlContents)
    
            ' Update the total.
            total += urlContents.Length
        Next 
    
        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "Total bytes returned:  {0}" & vbCrLf, total)
    End Sub 
    
    
    Private Function SetUpURLList() As List(Of String)
    
        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 
    
    
    Private Function GetURLContents(url As String) As Byte()
    
        ' The downloaded resource ends up in the variable named content. 
        Dim content = New MemoryStream()
    
        ' Initialize an HttpWebRequest for the current URL. 
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)
    
        ' Send the request to the Internet resource and wait for 
        ' the response. 
        ' Note: you can't use HttpWebRequest.GetResponse in a Windows Store app. 
        Using response As WebResponse = webReq.GetResponse()
            ' Get the data stream that is associated with the specified URL. 
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.  
                responseStream.CopyTo(content)
            End Using 
        End Using 
    
        ' Return the result as a byte array. 
        Return content.ToArray()
    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
    
    private void SumPageSizes()
    {
        // Make a list of web addresses.
        List<string> urlList = SetUpURLList(); 
    
        var total = 0;
        foreach (var url in urlList)
        {
            // GetURLContents returns the contents of url as a byte array. 
            byte[] urlContents = GetURLContents(url);
    
            DisplayResults(url, urlContents);
    
            // Update the total.
            total += urlContents.Length;
        }
    
        // Display the total count for all of the web addresses.
        resultsTextBox.Text += 
            string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
    }
    
    
    private List<string> SetUpURLList()
    {
        var urls = new List<string> 
        { 
            "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
            "https://msdn.microsoft.com",
            "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
            "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
            "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
            "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
            "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
            "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
        };
        return urls;
    }
    
    
    private byte[] GetURLContents(string url)
    {
        // The downloaded resource ends up in the variable named content. 
        var content = new MemoryStream();
    
        // Initialize an HttpWebRequest for the current URL. 
        var webReq = (HttpWebRequest)WebRequest.Create(url);
    
        // Send the request to the Internet resource and wait for 
        // the response. 
        // Note: you can't use HttpWebRequest.GetResponse in a Windows Store app. 
        using (WebResponse response = webReq.GetResponse())
        {
            // Get the data stream that is associated with the specified URL. 
            using (Stream responseStream = response.GetResponseStream())
            {
                // Read the bytes in responseStream and copy them to content.  
                responseStream.CopyTo(content);
            }
        }
    
        // Return the result as a byte array. 
        return content.ToArray();
    }
    
    
    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);
    }
    

Проверка синхронного решения

  • Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Start.

    Появятся выходные данные, подобные приведенным ниже.

    msdn.microsoft.com/library/windows/apps/br211380.aspx        383832
    msdn.microsoft.com                                            33964
    msdn.microsoft.com/en-us/library/hh290136.aspx               225793
    msdn.microsoft.com/en-us/library/ee256749.aspx               143577
    msdn.microsoft.com/en-us/library/hh290138.aspx               237372
    msdn.microsoft.com/en-us/library/hh290140.aspx               128279
    msdn.microsoft.com/en-us/library/dd470362.aspx               157649
    msdn.microsoft.com/en-us/library/aa578028.aspx               204457
    msdn.microsoft.com/en-us/library/ms404677.aspx               176405
    msdn.microsoft.com/en-us/library/ff730837.aspx               143474
    
    Total bytes returned:  1834802
    
    Control returned to startButton_Click.
    

    Обратите внимание, что пройдет несколько секунд, прежде чем данные отобразятся. Во время этого поток пользовательского интерфейса блокирован в ожидании запрошенных загружаемых ресурсов. В результате нельзя переместить развернуть, свернуть или даже закрыть окно после выбора кнопки Начать. Эти усилия бесполезны до тех пор, пока не появится счетчик байтов. Если веб-сайт не отвечает, то не будет никаких признаков того, какой сайт не удалось загрузить. Трудно даже прекратить ожидание и закрыть программу.

    Сравните это поведение с Пример для асинхронных решений.

Чтобы преобразовать метод GetURLContents в асинхронный

  1. Для преобразования синхронного решения в асинхронное, лучше всего начать с GetURLContents, так как вызовы метода HttpWebRequest GetResponse и метода Stream CopyTo используются тогда, когда приложение обращается к сети. .NET Framework упрощает преобразование путем предоставления асинхронных версий обоих методов.

    Дополнительные сведения о методах, используемых в GetURLContents, см. в разделе WebRequest.

    Примечание

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

    Измените метод, который вызывается в третьей строке GetURLContents с GetResponse на асинхронный ориентированный на задачи метод GetResponseAsync.

    Using response As WebResponse = webReq.GetResponseAsync()
    
    using (WebResponse response = webReq.GetResponseAsync())
    
  2. GetResponseAsync возвращает Task. В этом случае возвращаемая переменная задачи, TResult, имеет тип WebResponse. Задача — обещание создать объект WebResponse после того, как необходимые данные были загружены, а задача выполнена до завершения.

    Для получения значения WebResponse из задачи, примените оператор Await (Visual Basic) или await (C#) к вызову GetResponseAsync, как показано в следующем коде.

    Using response As WebResponse = Await webReq.GetResponseAsync()
    
    using (WebResponse response = await webReq.GetResponseAsync())
    

    Оператор await не будет приостанавливать выполнение текущего метода, GetURLContents, до завершения ожидаемой задачи. Тем временем, управление возвращается к вызывающему объекту текущего метода. В следующем примере текущий метод — GetURLContents, а вызывающий — SumPageSizes. Когда задача завершена, обещанный объект WebResponse создается в качестве значения ожидаемой задачи и присваивается переменной response.

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

    'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync() 
    'Using response As WebResponse = Await responseTask
    
    //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
    //using (WebResponse response = await responseTask)
    

    Вызов webReq.GetResponseAsync возвращает Task(Of WebResponse) или Task<WebResponse>. Затем оператор await применения к задаче для извлечения значения WebResponse.

    Если асинхронный метод имеет работу, которая не зависит от завершения задачи, то метод может продолжать выполнять эту работу между этими двумя выражениями — после вызова асинхронного метода и до применения оператора async. Примеры см. в разделах Практическое руководство. Параллельное выполнение нескольких веб-запросов с использованием Async и Await (C# и Visual Basic) и Практическое руководство. Расширение пошагового руководства по асинхронным процедурам с использованием метода Task.WhenAll (C# и Visual Basic).

  3. Так как на предыдущем шаге был добавлен оператор Await или await, то возникает ошибка компилятора. Оператор может использоваться только в методах, которые помечены модификатором Async (Visual Basic) или async (C#). Игнорируйте ошибки пока выполняете шаги преобразования по замене вызова CopyTo на вызов CopyToAsync.

    • Измените имя вызываемого метода на CopyToAsync.

    • Метод CopyTo или CopyToAsync копирует байты в свой аргумент content и не возвращает понятного значения. В синхронной версии вызов CopyTo — это простое выражение, не возвращающее значения. Асинхронная версия, CopyToAsync, возвращает Task. Функции задачи выглядят подобно "Task(void)" и позволяют ожидать метод. Примените Await или await для вызова CopyToAsync, как показано ниже.

      Await responseStream.CopyToAsync(content)
      
      await responseStream.CopyToAsync(content);
      

      Предыдущее выражение сокращает следующие две строки кода.

      ' CopyToAsync returns a Task, not a Task<T>. 
      'Dim copyTask As Task = responseStream.CopyToAsync(content) 
      
      ' When copyTask is completed, content contains a copy of 
      ' responseStream. 
      'Await copyTask
      
      // CopyToAsync returns a Task, not a Task<T>. 
      //Task copyTask = responseStream.CopyToAsync(content); 
      
      // When copyTask is completed, content contains a copy of 
      // responseStream. 
      //await copyTask;
      
  4. Все, что остается сделать в GetURLContents, — это изменить сигнатуру метода. Оператор Await или await можно использовать только в методах, помеченных модификатором Async (Visual Basic) или async (C#). Добавьте модификатор для того, чтобы пометить метод как асинхронный, как показано ниже.

    Private Async Function GetURLContents(url As String) As Byte()
    
    private async byte[] GetURLContents(string url)
    
  5. Тип возвращаемого значения асинхронного метода может быть только Task, Task или void в C#. В Visual Basic, метод должен быть Function, которая возвращает Task или Task(Of T), или метод должен быть Sub. Как правило, метод Sub (Visual Basic) или возвращаемый тип void (C#) используются только в обработчике асинхронных событий, где требуются Sub или void. В других случаях, используется Task(T), если завершенный метод имеет выражение Return или return, которое возвращает значение типа T, и используется Task, если завершенный метод не возвращает значимое значение. Возвращаемый тип Task можно рассматривать как "Task(void)".

    Для получения дополнительной информации см. Асинхронные типы возвращаемых значений (C# и Visual Basic).

    Метод GetURLContents имеет выражение return, и оно возвращает массив байтов. Таким образом, тип возвращаемого значения асинхронной версии — Task(T), где T — массив байтов. Внесите в сигнатуру метода следующие изменения:

    • Измените возвращаемый тип на Task(Of Byte()) (Visual Basic) или Task<byte[]> (C#).

    • По соглашению, имена асинхронных методов оканчиваются на "Async", поэтому переименуйте метод GetURLContentsAsync.

    Приведенный ниже код показывает эти изменения.

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())
    
    private async Task<byte[]> GetURLContentsAsync(string url)
    

    Эти незначительные изменения завершают преобразование метода GetURLContents в асинхронный.

Чтобы преобразовать метод SumPageSizes в асинхронный

  1. Повторите шаги из предыдущей процедуры для SumPageSizes. Во-первых, измените вызов GetURLContents на асинхронный.

    • Измените имя метода, который вызывается из GetURLContents на GetURLContentsAsync, если вы это еще не сделали.

    • Примените Await или await к задаче, которую возвращает GetURLContentsAsync для получения значения массива байтов.

    Приведенный ниже код показывает эти изменения.

    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    
    byte[] urlContents = await GetURLContentsAsync(url);
    

    Предыдущее присваивание сокращает следующие две строки кода.

    ' GetURLContentsAsync returns a task. At completion, the task 
    ' produces a byte array. 
    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url) 
    'Dim urlContents As Byte() = Await getContentsTask
    
    // GetURLContentsAsync returns a Task<T>. At completion, the task 
    // produces a byte array. 
    //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
    //byte[] urlContents = await getContentsTask;
    
  2. Внесите в сигнатуру метода следующие изменения:

    • Пометьте метод модификатором Async или async.

    • Добавьте "Async" в имя метода.

    • На этот раз нет возвращаемой переменной задачи, так как SumPageSizesAsync не возвращает значения для T. (У метода нет выражения Return или return.) Тем не менее, метод должен возвращать Task для того, чтобы его можно было ожидать. Поэтому сделайте одно из следующих изменений:

      • В Visual Basic, измените тип метода с Sub на Function. Возвращаемый тип функции — Task.

      • В C# следует изменить тип возвращаемого значения метода с void на Task.

    Приведенный ниже код показывает эти изменения.

    Private Async Function SumPageSizesAsync() As Task
    
    private async Task SumPageSizesAsync()
    

    Преобразование SumPageSizes в SumPageSizesAsync завершено.

Чтобы преобразовать метод startButton_Click в асинхронный

  1. В обработчике событий измените имя вызванного метода с SumPageSizes на SumPageSizesAsync, если это еще не сделано.

  2. Так как метод SumPageSizesAsync асинхронный, измените код в обработчике событий для ожидания результата.

    Вызов SumPageSizesAsync отражает вызов CopyToAsync в GetURLContentsAsync. Вызов возвращает Task, а не Task(T).

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

    '' One-step async call.
    Await SumPageSizesAsync()
    
    ' Two-step async call. 
    'Dim sumTask As Task = SumPageSizesAsync() 
    'Await sumTask
    
    // One-step async call.
    await SumPageSizesAsync();
    
    // Two-step async call. 
    //Task sumTask = SumPageSizesAsync(); 
    //await sumTask;
    
  3. Для предотвращения случайного reentering операции добавьте следующую выписка в верхней части startButton_Click, чтобы отключить кнопку Запуск.

    ' Disable the button until the operation is complete.
    startButton.IsEnabled = False
    
    // Disable the button until the operation is complete.
    startButton.IsEnabled = false;
    

    Можно снова включить кнопку в конце обработчика событий.

    ' Reenable the button in case you want to run the operation again.
    startButton.IsEnabled = True
    
    // Reenable the button in case you want to run the operation again.
    startButton.IsEnabled = true;
    

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

  4. Наконец, добавьте модификатор Async или async к объявлению, чтобы обработчик событий мог ожидать SumPagSizesAsync.

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click
    
    private async void startButton_Click(object sender, RoutedEventArgs e)
    

    Как правило, имена обработчиков событий не изменяют. Возвращаемый тип не изменяется на Task, поскольку обработчики событий должны возвращать void в C# или должны быть процедурами Sub в Visual Basic. Поэтому возвращаемый тип Task.

    Преобразование проекта из синхронной обработки в асинхронную завершено.

Тестирование асинхронного решения

  1. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Start.

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

    • Результаты появляются не все сразу и одновременно после завершения обработки. Например, обе программы содержат строку в startButton_Click, которая очищает текстовое поле. Цель состоит в том, чтобы очищать текстовое поле между запусками, если вы нажмете кнопку Start во второй раз после отображения одного набора результатов. В синхронной версии текстовое поле очищается непосредственно перед появлением счетчиков во второй раз, когда загрузки завершены, а поток пользовательского интерфейса свободен и готов выполнять другую работу. В асинхронной версии текстовое поле очищается сразу после выбора кнопки Начать.

    • Наиболее важно то, что поток пользовательского интерфейса не блокируется во время загрузок. Можно перемещать или изменять размер окна во время загрузки веб-ресурсов, их подсчета и отображения. Если один из веб-сайтов медленный или не отвечает, то можно отменить операцию с помощью кнопки Закрыть (x на красном фоне в верхнем правом углу).

Замена метода GetURLContentsAsync методом .NET Framework

  1. .NET Framework 4.5 предоставляет множество асинхронных методов, которые можно использовать. Один из них, метод HttpClient GetByteArrayAsync(String), выполняет то, что нужно для этого пошагового руководства. Его можно использовать вместо метода GetURLContentsAsync, созданного в предыдущей процедуре.

    Первым шагом является создание объекта HttpClient в методе SumPageSizesAsync. Добавьте следующее объявление в начало метода.

    ' 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}
    
    // Declare an HttpClient object and increase the buffer size. The 
    // default buffer size is 65,536.
    HttpClient client =
        new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    
  2. В SumPageSizesAsync, замените вызов вашего метода GetURLContentsAsync вызовом метода HttpClient.

    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
    byte[] urlContents = await client.GetByteArrayAsync(url);               
    
  3. Удалите или закомментируйте метод GetURLContentsAsync, который был написан вручную.

  4. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Start.

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

Пример

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

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

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False

        resultsTextBox.Clear()

        '' One-step async call.
        Await SumPageSizesAsync()

        ' Two-step async call. 
        'Dim sumTask As Task = SumPageSizesAsync() 
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click." 

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True 
    End Sub 


    Private Async Function SumPageSizesAsync() As Task

        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            Dim urlContents As Byte() = Await GetURLContentsAsync(url)

            ' The previous line abbreviates the following two assignment statements. 

            ' GetURLContentsAsync returns a task. At completion, the task 
            ' produces a byte array. 
            'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url) 
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next 

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


    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 


    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

        ' The downloaded resource ends up in the variable named content. 
        Dim content = New MemoryStream()

        ' Initialize an HttpWebRequest for the current URL. 
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)

        ' Send the request to the Internet resource and wait for 
        ' the response. 
        Using response As WebResponse = Await webReq.GetResponseAsync()

            ' The previous statement abbreviates the following two statements. 

            'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync() 
            'Using response As WebResponse = Await responseTask 

            ' Get the data stream that is associated with the specified URL. 
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.  
                Await responseStream.CopyToAsync(content)

                ' The previous statement abbreviates the following two statements. 

                ' CopyToAsync returns a Task, not a Task<T>. 
                'Dim copyTask As Task = responseStream.CopyToAsync(content) 

                ' When copyTask is completed, content contains a copy of 
                ' responseStream. 
                'Await copyTask 
            End Using 
        End Using 

        ' Return the result as a byte array. 
        Return content.ToArray()
    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 directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;

namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            resultsTextBox.Clear();

            // One-step async call.
            await SumPageSizesAsync();

            // Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                byte[] urlContents = await GetURLContentsAsync(url);

                // The previous line abbreviates the following two assignment statements. 

                // GetURLContentsAsync returns a Task<T>. At completion, the task 
                // produces a byte array. 
                //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.          
                total += urlContents.Length;
            }
            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        private async Task<byte[]> GetURLContentsAsync(string url)
        {
            // The downloaded resource ends up in the variable named content. 
            var content = new MemoryStream();

            // Initialize an HttpWebRequest for the current URL. 
            var webReq = (HttpWebRequest)WebRequest.Create(url);

            // Send the request to the Internet resource and wait for 
            // the response.                 
            using (WebResponse response = await webReq.GetResponseAsync())

            // The previous statement abbreviates the following two statements. 

            //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
            //using (WebResponse response = await responseTask)
            {
                // Get the data stream that is associated with the specified url. 
                using (Stream responseStream = response.GetResponseStream())
                {
                    // Read the bytes in responseStream and copy them to content. 
                    await responseStream.CopyToAsync(content);

                    // The previous statement abbreviates the following two statements. 

                    // CopyToAsync returns a Task, not a Task<T>. 
                    //Task copyTask = responseStream.CopyToAsync(content); 

                    // When copyTask is completed, content contains a copy of 
                    // responseStream. 
                    //await copyTask;
                }
            }
            // Return the result as a byte array. 
            return content.ToArray();
        }


        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);
        }
    }
}

Следующий код содержит полный пример решения, которое использует метод HttpClient, GetByteArrayAsync.

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

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False 

        ' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call. 
        'Dim sumTask As Task = SumPageSizesAsync() 
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click." 

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True 
    End Sub 


    Private Async Function SumPageSizesAsync() 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}

        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            ' GetByteArrayAsync returns a task. At completion, the task 
            ' produces a byte array. 
            Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)

            ' The following two lines can replace the previous assignment statement. 
            'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url) 
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next 

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


    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    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 directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;


namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();

            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            // One-step async call.
            await SumPageSizesAsync();

            //// Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Declare an HttpClient object and increase the buffer size. The 
            // default buffer size is 65,536.
            HttpClient client =
                new HttpClient() { MaxResponseContentBufferSize = 1000000 };

            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                // GetByteArrayAsync returns a task. At completion, the task 
                // produces a byte array. 
                byte[] urlContents = await client.GetByteArrayAsync(url);               

                // The following two lines can replace the previous assignment statement. 
                //Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.
                total += urlContents.Length;
            }

            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        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);
        }
    }
}

См. также

Задачи

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

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

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

Ссылки

async (справочник по C#)

await (Справочник по C#)

Оператор Await (Visual Basic)

Async (Visual Basic)

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

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

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

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

Другие ресурсы

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

Асинхронное программирование, основанное на задачах (TAP)

Краткое руководство: использование оператора await для асинхронного программирования