Пошаговое руководство. Доступ к Интернету с помощью модификатора Async и оператора Await (C#)Walkthrough: Accessing the Web by Using async and await (C#)

Возможности Async и Await упрощают создание асинхронных программ.You can write asynchronous programs more easily and intuitively by using async/await features. Можно написать асинхронный код, который выглядит как синхронный, и позволить компилятору обрабатывать трудные функции обратного вызова и продолжения, которые обычно включает асинхронный код.You can write asynchronous code that looks like synchronous code and let the compiler handle the difficult callback functions and continuations that asynchronous code usually entails.

Дополнительные сведения о возможности Async см. в разделе Асинхронное программирование с использованием ключевых слов Async и Await (C#).For more information about the Async feature, see Asynchronous Programming with async and await (C#).

Это пошаговое руководство начинается с создания синхронного приложения Windows Presentation Foundation (WPF), которое суммирует число байтов в списке веб-сайтов.This walkthrough starts with a synchronous Windows Presentation Foundation (WPF) application that sums the number of bytes in a list of websites. Затем в рамках руководства приложение преобразуется в асинхронное решение с помощью новых возможностей.The walkthrough then converts the application to an asynchronous solution by using the new features.

Если вы не хотите создавать приложение самостоятельно, см. пошаговое руководство по доступу к Интернету и использованию Async (C# и Visual Basic).If you don't want to build the applications yourself, you can download Async Sample: Accessing the Web Walkthrough (C# and Visual Basic).

Примечание

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

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

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

  2. В строке меню выберите Файл > Создать > Проект.On the menu bar, choose File > New > Project.

    Откроется диалоговое окно Новый проект .The New Project dialog box opens.

  3. В области Установленные шаблоны выберите "Visual C#", а затем в списке типов проектов выберите Приложение WPF.In the Installed Templates pane, choose Visual C#, and then choose WPF Application from the list of project types.

  4. В текстовом поле Имя введите AsyncExampleWPF и нажмите кнопку ОК.In the Name text box, enter AsyncExampleWPF, and then choose the OK button.

    В обозревателе решений появится новый проект.The new project appears in Solution Explorer.

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

  1. В редакторе кода Visual Studio перейдите на вкладку MainWindow.xaml .In the Visual Studio Code Editor, choose the MainWindow.xaml tab.

  2. Если окно Панель элементов не отображается, в меню Вид выберите пункт Панель элементов.If the Toolbox window isn’t visible, open the View menu, and then choose Toolbox.

  3. Добавьте элементы управления Button и TextBox в окно MainWindow.Add a Button control and a TextBox control to the MainWindow window.

  4. Выделите элемент управления TextBox и в окне Свойства задайте указанные ниже значения.Highlight the TextBox control and, in the Properties window, set the following values:

    • Задайте для свойства Имя значение resultsTextBox.Set the Name property to resultsTextBox.

    • Задайте для свойства Высота значение 250.Set the Height property to 250.

    • Задайте для свойства Ширина значение 500.Set the Width property to 500.

    • На вкладке Текст укажите моноширинный шрифт, например Lucida Console или Global Monospace.On the Text tab, specify a monospaced font, such as Lucida Console or Global Monospace.

  5. Выделите элемент управления Button и в окне Свойства задайте указанные ниже значения.Highlight the Button control and, in the Properties window, set the following values:

    • Задайте для свойства Имя значение startButton.Set the Name property to startButton.

    • Измените значение свойства Содержимое с Button на Start.Change the value of the Content property from Button to Start.

  6. Поместите текстовое поле и кнопку так, чтобы оба элемента управления отображались в окне MainWindow.Position the text box and the button so that both appear in the MainWindow window.

    Дополнительные сведения о конструкторе XAML WPF см. в разделе Создание пользовательского интерфейса с помощью конструктора XAML.For more information about the WPF XAML Designer, see Creating a UI by using XAML Designer.

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

  1. В обозревателе решений выделите имя проекта.In Solution Explorer, highlight your project's name.

  2. В строке меню выберите Проект > Добавить ссылку.On the menu bar, choose Project > Add Reference.

    Откроется диалоговое окно Диспетчер ссылок.The Reference Manager dialog box appears.

  3. Вверху диалогового окна убедитесь в том, что проект предназначен для платформы .NET Framework 4.5 или более поздней версии.At the top of the dialog box, verify that your project is targeting the .NET Framework 4.5 or higher.

  4. В категории Сборки выберите Платформа, если этот вариант еще не выбран.In the Assemblies category, choose Framework if it isn’t already chosen.

  5. В списке имен установите флажок System.Net.Http.In the list of names, select the System.Net.Http check box.

  6. Нажмите кнопку ОК, чтобы закрыть диалоговое окно.Choose the OK button to close the dialog box.

Добавление необходимых директив usingAdd necessary using directives

  1. В обозревателе решений откройте контекстное меню для MainWindow.xaml.cs и выберите пункт Просмотреть код.In Solution Explorer, open the shortcut menu for MainWindow.xaml.cs, and then choose View Code.

  2. В начало файла с кодом добавьте указанные ниже директивы using, если они отсутствуют.Add the following using directives at the top of the code file if they’re not already present.

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

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

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

  2. В файле MainWindow.xaml.cs скопируйте следующий код в тело startButton_Click:In MainWindow.xaml.cs, copy the following code into the body of startButton_Click:

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

    Код вызывает метод, который управляет приложением SumPageSizes и выводит на экран сообщение, когда элемент управления возвращается к startButton_Click.The code calls the method that drives the application, SumPageSizes, and displays a message when control returns to startButton_Click.

  3. Код для синхронного решения содержит следующие четыре метода:The code for the synchronous solution contains the following four methods:

    • SumPageSizes, который получает список URL-адресов веб-страниц из SetUpURLList, а затем вызывает метод GetURLContents и DisplayResults для обработки каждого URL-адреса;SumPageSizes, which gets a list of webpage URLs from SetUpURLList and then calls GetURLContents and DisplayResults to process each URL.

    • SetUpURLList, который создает и возвращает список веб-адресов;SetUpURLList, which makes and returns a list of web addresses.

    • GetURLContents, который загружает содержимое каждого веб-сайта и возвращает содержимое в виде массива байтов;GetURLContents, which downloads the contents of each website and returns the contents as a byte array.

    • DisplayResults, который показывает число байтов в массиве байтов для каждого URL-адреса.DisplayResults, which displays the number of bytes in the byte array for each URL.

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

    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 += $"\r\n\r\nTotal bytes returned:  {total}\r\n";
    }
    
    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/library/hh290136.aspx",
            "https://msdn.microsoft.com/library/ee256749.aspx",
            "https://msdn.microsoft.com/library/hh290138.aspx",
            "https://msdn.microsoft.com/library/hh290140.aspx",
            "https://msdn.microsoft.com/library/dd470362.aspx",
            "https://msdn.microsoft.com/library/aa578028.aspx",
            "https://msdn.microsoft.com/library/ms404677.aspx",
            "https://msdn.microsoft.com/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 "https://".
        var displayURL = url.Replace("https://", "");
        resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
    }
    

Тестирование синхронного решенияTest the synchronous solution

Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Запуск.Choose the F5 key to run the program, and then choose the Start button.

Программа должна выдать результаты, похожие на следующий список:Output that resembles the following list should appear:

msdn.microsoft.com/library/windows/apps/br211380.aspx        383832
msdn.microsoft.com                                            33964
msdn.microsoft.com/library/hh290136.aspx               225793
msdn.microsoft.com/library/ee256749.aspx               143577
msdn.microsoft.com/library/hh290138.aspx               237372
msdn.microsoft.com/library/hh290140.aspx               128279
msdn.microsoft.com/library/dd470362.aspx               157649
msdn.microsoft.com/library/aa578028.aspx               204457
msdn.microsoft.com/library/ms404677.aspx               176405
msdn.microsoft.com/library/ff730837.aspx               143474

Total bytes returned:  1834802

Control returned to startButton_Click.

Обратите внимание, что вывод результатов на экран занимает несколько секунд.Notice that it takes a few seconds to display the counts. В течение этого времени поток пользовательского интерфейса заблокирован, пока он ожидает загрузку запрошенных ресурсов.During that time, the UI thread is blocked while it waits for requested resources to download. Соответственно, после нажатия кнопки Start окно нельзя перемещать, разворачивать, сворачивать или даже закрывать.As a result, you can't move, maximize, minimize, or even close the display window after you choose the Start button. Эти действия будут завершаться сбоем, пока не появятся результаты подсчета.These efforts fail until the byte counts start to appear. Если веб-сайт не отвечает, вы не можете определить, на каком сайте произошел сбой.If a website isn’t responding, you have no indication of which site failed. Трудно даже остановить ожидание и закрыть программу.It is difficult even to stop waiting and close the program.

Преобразование GetURLContents в асинхронный методConvert GetURLContents to an asynchronous method

  1. Чтобы преобразовать синхронное решение в асинхронное, лучше всего начать с метода GetURLContents, поскольку вызовы метода HttpWebRequest GetResponse и метода Stream CopyTo выполняются, когда приложение подключается к Интернету.To convert the synchronous solution to an asynchronous solution, the best place to start is in GetURLContents because the calls to the HttpWebRequest method GetResponse and to the Stream method CopyTo are where the application accesses the web. Платформа .NET Framework упрощает преобразование путем предоставления асинхронных версий этих методов.The .NET Framework makes the conversion easy by supplying asynchronous versions of both methods.

    Дополнительные сведения о методах, которые используются в GetURLContents, см. в разделе WebRequest.For more information about the methods that are used in GetURLContents, see WebRequest.

    Примечание

    По мере выполнения шагов в этом пошаговом руководстве возникают различные ошибки компилятора.As you follow the steps in this walkthrough, several compiler errors appear. Их можно игнорировать и продолжить процедуры пошагового руководства.You can ignore them and continue with the walkthrough.

    Измените метод, который вызывается в третьей строке GetURLContents из GetResponse, асинхронным методом GetResponseAsync, основанным на задачах.Change the method that's called in the third line of GetURLContents from GetResponse to the asynchronous, task-based GetResponseAsync method.

    using (WebResponse response = webReq.GetResponseAsync())
    
  2. GetResponseAsync возвращает значение типа Task<TResult>.GetResponseAsync returns a Task<TResult>. В этом случае переменная, возвращаемая задачей, TResult, имеет тип WebResponse.In this case, the task return variable, TResult, has type WebResponse. Задача является обещанием создать фактический объект WebResponse после загрузки запрошенных данных и выполнения задачи до завершения.The task is a promise to produce an actual WebResponse object after the requested data has been downloaded and the task has run to completion.

    Для получения значения WebResponse из задачи примените оператор await для вызова метода GetResponseAsync, как показано в приведенном ниже примере кода.To retrieve the WebResponse value from the task, apply an await operator to the call to GetResponseAsync, as the following code shows.

    using (WebResponse response = await webReq.GetResponseAsync())
    

    Оператор await приостанавливает выполнение текущего метода GetURLContents, пока не будет завершена ожидаемая задача.The await operator suspends the execution of the current method, GetURLContents, until the awaited task is complete. На это время управление возвращается вызывающему объекту текущего метода.In the meantime, control returns to the caller of the current method. В этом примере текущим методом является GetURLContents, а вызывающим объектом — SumPageSizes.In this example, the current method is GetURLContents, and the caller is SumPageSizes. После завершения задачи создается обещанный объект WebResponse в качестве значения ожидаемой задачи, который присваивается переменной response.When the task is finished, the promised WebResponse object is produced as the value of the awaited task and assigned to the variable response.

    Предыдущий оператор можно разделить на следующие два оператора для уточнения выполняемых операций.The previous statement can be separated into the following two statements to clarify what happens.

    //Task<WebResponse> responseTask = webReq.GetResponseAsync();
    //using (WebResponse response = await responseTask)
    

    Вызов webReq.GetResponseAsync возвращает Task(Of WebResponse) или Task<WebResponse>.The call to webReq.GetResponseAsync returns a Task(Of WebResponse) or Task<WebResponse>. Затем оператор await применяется к задаче для получения значения WebResponse.Then an await operator is applied to the task to retrieve the WebResponse value.

    Если асинхронному методу нужно выполнить определенную работу, не связанную с завершением конкретной задачи, он может проделать это между выполнением этих двух операторов: после вызова метода async и перед применением оператора await.If your async method has work to do that doesn’t depend on the completion of the task, the method can continue with that work between these two statements, after the call to the async method and before the await operator is applied. Примеры см. в статьях Практическое руководство. Параллельное выполнение нескольких веб-запросов с использованием Async и Await (C#) и Практическое руководство. Расширение пошагового руководства по асинхронным процедурам с использованием метода Task.WhenAll (C#).For examples, see How to make multiple web requests in parallel by using async and await (C#) and How to extend the async walkthrough by using Task.WhenAll (C#).

  3. Из-за добавления оператора await в предыдущем шаге возникает ошибка компилятора.Because you added the await operator in the previous step, a compiler error occurs. Этот оператор можно использовать только в методах, помеченных модификатором async.The operator can be used only in methods that are marked with the async modifier. Пропустите ошибку, повторяя действия по замене вызова CopyTo вызовом метода CopyToAsync.Ignore the error while you repeat the conversion steps to replace the call to CopyTo with a call to CopyToAsync.

    • Измените имя метода, вызывающего CopyToAsync.Change the name of the method that’s called to CopyToAsync.

    • Метод CopyTo или CopyToAsync копирует байты в свой аргумент content и не возвращает осмысленное значение.The CopyTo or CopyToAsync method copies bytes to its argument, content, and doesn’t return a meaningful value. В синхронной версии вызов метода CopyTo — это просто оператор, который не возвращает значение.In the synchronous version, the call to CopyTo is a simple statement that doesn't return a value. Асинхронная версия — CopyToAsync — возвращает Task.The asynchronous version, CopyToAsync, returns a Task. Задача работает как Task(void) и позволяет ожидать метод.The task functions like "Task(void)" and enables the method to be awaited. Примените Await или await к вызову CopyToAsync, как показано в следующем примере кода.Apply Await or await to the call to CopyToAsync, as the following code shows.

      await responseStream.CopyToAsync(content);
      

      Предыдущий оператор сокращает две следующие строки кода.The previous statement abbreviates the following two lines of code.

      // 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, — это изменить подпись метода.All that remains to be done in GetURLContents is to adjust the method signature. Оператор await можно использовать только в методах, помеченных модификатором async.You can use the await operator only in methods that are marked with the async modifier. Добавьте модификатор, чтобы пометить метод как асинхронный, как показано в приведенном ниже примере кода.Add the modifier to mark the method as an async method, as the following code shows.

    private async byte[] GetURLContents(string url)
    
  5. Типом возвращаемого значения асинхронного метода может быть только Task, Task<TResult> или void в C#.The return type of an async method can only be Task, Task<TResult>, or void in C#. Как правило, тип возвращаемого значения void используется только в асинхронном обработчике событий, где void является обязательным.Typically, a return type of void is used only in an async event handler, where void is required. В других случаях используется Task(T), если завершенный метод имеет оператор return, возвращающий значение типа T, или Task, если завершенный метод не возвращает осмысленное значение.In other cases, you use Task(T) if the completed method has a return statement that returns a value of type T, and you use Task if the completed method doesn’t return a meaningful value. Можно представить тип возвращаемого значения Task как Task(void).You can think of the Task return type as meaning "Task(void)."

    Дополнительные сведения см. в разделе Асинхронные типы возвращаемых значений (C#).For more information, see Async Return Types (C#).

    Метод GetURLContents имеет оператор return, который возвращает массив байтов.Method GetURLContents has a return statement, and the statement returns a byte array. Таким образом, тип возвращаемого значения асинхронной версии — Task(T), где T — массив байтов.Therefore, the return type of the async version is Task(T), where T is a byte array. Внесите следующие изменения в подпись метода.Make the following changes in the method signature:

    • Измените тип возвращаемого значения на Task<byte[]>.Change the return type to Task<byte[]>.

    • По соглашению об именовании асинхронные методы имеют имена, заканчивающиеся на Async, поэтому переименуйте метод в GetURLContentsAsync.By convention, asynchronous methods have names that end in "Async," so rename the method GetURLContentsAsync.

    В следующем примере кода показаны эти изменения.The following code shows these changes.

    private async Task<byte[]> GetURLContentsAsync(string url)
    

    После внесения этих изменений преобразование GetURLContents в асинхронный метод завершено.With those few changes, the conversion of GetURLContents to an asynchronous method is complete.

Преобразование SumPageSizes в асинхронный методConvert SumPageSizes to an asynchronous method

  1. Повторите шаги из предыдущей процедуры для SumPageSizes.Repeat the steps from the previous procedure for SumPageSizes. Во-первых, преобразуйте вызов метода GetURLContents в вызов асинхронного метода.First, change the call to GetURLContents to an asynchronous call.

    • Измените имя вызываемого метода с GetURLContents на GetURLContentsAsync, если это еще не сделано.Change the name of the method that’s called from GetURLContents to GetURLContentsAsync, if you haven't already done so.

    • Примените оператор await к задаче, возвращаемой методом GetURLContentsAsync, для получения значения байтового массива.Apply await to the task that GetURLContentsAsync returns to obtain the byte array value.

    В следующем примере кода показаны эти изменения.The following code shows these changes.

    byte[] urlContents = await GetURLContentsAsync(url);
    

    Предыдущее назначение сокращает две следующие строки кода.The previous assignment abbreviates the following two lines of code.

    // GetURLContentsAsync returns a Task<T>. At completion, the task
    // produces a byte array.
    //Task<byte[]> getContentsTask = GetURLContentsAsync(url);
    //byte[] urlContents = await getContentsTask;
    
  2. Внесите следующие изменения в подпись метода.Make the following changes in the method's signature:

    • Пометьте метод модификатором async.Mark the method with the async modifier.

    • Добавьте в имя метода Async.Add "Async" to the method name.

    • В этот раз нет возвращаемой переменной задачи T, поскольку SumPageSizesAsync не возвращает значение для T. (В этом методе нет оператора return.) Тем не менее метод должен возвращать Task, чтобы для него можно было задать ожидание.There is no task return variable, T, this time because SumPageSizesAsync doesn’t return a value for T. (The method has no return statement.) However, the method must return a Task to be awaitable. Поэтому измените тип возвращаемого значения метода с void на Task.Therefore, change the return type of the method from void to Task.

    В следующем примере кода показаны эти изменения.The following code shows these changes.

    private async Task SumPageSizesAsync()
    

    Преобразование SumPageSizes в SumPageSizesAsync завершено.The conversion of SumPageSizes to SumPageSizesAsync is complete.

Преобразование startButton_Click в асинхронный методConvert startButton_Click to an asynchronous method

  1. В обработчике событий измените имя вызываемого метода с SumPageSizes на SumPageSizesAsync, если это еще не сделано.In the event handler, change the name of the called method from SumPageSizes to SumPageSizesAsync, if you haven’t already done so.

  2. Поскольку SumPageSizesAsync является асинхронным методом, измените код в обработчике событий для ожидания результата.Because SumPageSizesAsync is an async method, change the code in the event handler to await the result.

    Вызов SumPageSizesAsync отражает вызов CopyToAsync в GetURLContentsAsync.The call to SumPageSizesAsync mirrors the call to CopyToAsync in GetURLContentsAsync. Вызов возвращает Task, а не Task(T).The call returns a Task, not a Task(T).

    Как и в предыдущих процедурах, вызов можно преобразовать, используя один или два оператора.As in previous procedures, you can convert the call by using one statement or two statements. В следующем примере кода показаны эти изменения.The following code shows these changes.

    // One-step async call.
    await SumPageSizesAsync();
    
    // Two-step async call.
    //Task sumTask = SumPageSizesAsync();
    //await sumTask;
    
  3. Чтобы предотвратить случайное повторное введение операции, добавьте следующий оператор в верхней части startButton_Click, чтобы отключить кнопку Start.To prevent accidentally reentering the operation, add the following statement at the top of startButton_Click to disable the Start button.

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

    Можно снова включить кнопку в конце обработчика событий.You can reenable the button at the end of the event handler.

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

    Дополнительные сведения о повторном входе см. в разделе Обработка повторного входа в асинхронных приложениях (C#).For more information about reentrancy, see Handling Reentrancy in Async Apps (C#).

  4. Наконец, добавьте модификатор async в объявление, чтобы обработчик событий мог ожидать SumPagSizesAsync.Finally, add the async modifier to the declaration so that the event handler can await SumPagSizesAsync.

    private async void startButton_Click(object sender, RoutedEventArgs e)
    

    Как правило, имена обработчиков событий не изменяются.Typically, the names of event handlers aren’t changed. Тип возвращаемого значения не изменяется на Task, так как обработчики событий должны возвращать void.The return type isn’t changed to Task because event handlers must return void.

    Преобразование проекта из синхронного в асинхронный завершено.The conversion of the project from synchronous to asynchronous processing is complete.

Тестирование асинхронного решенияTest the asynchronous solution

  1. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Запуск.Choose the F5 key to run the program, and then choose the Start button.

  2. Появившиеся результаты должны напоминать результаты синхронного решения.Output that resembles the output of the synchronous solution should appear. Однако имеются следующие различия.However, notice the following differences.

    • Все результаты не отображаются одновременно после завершения обработки.The results don’t all occur at the same time, after the processing is complete. Например, обе программы содержат строку в startButton_Click, которая очищает текстовое поле.For example, both programs contain a line in startButton_Click that clears the text box. Ее цель состоит в том, чтобы очистить текстовое поле между запусками при нажатии кнопки Start во второй раз после появления одного набора результатов.The intent is to clear the text box between runs if you choose the Start button for a second time, after one set of results has appeared. В синхронной версии текстовое поле очищается перед отображением данных во второй раз, после завершения загрузки и высвобождения потока пользовательского интерфейса для выполнения других операций.In the synchronous version, the text box is cleared just before the counts appear for the second time, when the downloads are completed and the UI thread is free to do other work. В асинхронной версии текстовое поле очищается сразу же после нажатия кнопки Start.In the asynchronous version, the text box clears immediately after you choose the Start button.

    • И что самое главное, поток пользовательского интерфейса не блокируется во время загрузки.Most importantly, the UI thread isn’t blocked during the downloads. Можно перемещать окно или изменять его размер во время загрузки, подсчета и отображения веб-ресурсов.You can move or resize the window while the web resources are being downloaded, counted, and displayed. Если один из веб-сайтов работает медленно или не отвечает, можно отменить операцию, нажав кнопку Закрыть (красный крестик в правом верхнем углу окна).If one of the websites is slow or not responding, you can cancel the operation by choosing the Close button (the x in the red field in the upper-right corner).

Замена GetURLContentsAsync методом .NET FrameworkReplace method GetURLContentsAsync with a .NET Framework method

  1. Платформа .NET Framework 4.5 предоставляет много асинхронных методов, которые вы можете использовать.The .NET Framework 4.5 provides many async methods that you can use. Один из них, метод GetByteArrayAsync(String) HttpClient, выполняет именно те операции, которые требуются для данного пошагового руководства.One of them, the HttpClient method GetByteArrayAsync(String), does just what you need for this walkthrough. Его можно использовать вместо метода GetURLContentsAsync, созданного в предыдущей процедуре.You can use it instead of the GetURLContentsAsync method that you created in an earlier procedure.

    Первым шагом является создание объекта HttpClient в методе SumPageSizesAsync.The first step is to create an HttpClient object in method SumPageSizesAsync. Добавьте следующее объявление в начале метода.Add the following declaration at the start of the method.

    // 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.In SumPageSizesAsync, replace the call to your GetURLContentsAsync method with a call to the HttpClient method.

    byte[] urlContents = await client.GetByteArrayAsync(url);
    
  3. Удалите или прокомментируйте метод GetURLContentsAsync, который вы написали.Remove or comment out the GetURLContentsAsync method that you wrote.

  4. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Запуск.Choose the F5 key to run the program, and then choose the Start button.

    Поведение этой версии проекта должно соответствовать поведению, которое описывается в процедуре "Тестирование асинхронного решения"; при этом с вашей стороны требуется даже меньше усилий.The behavior of this version of the project should match the behavior that the "To test the asynchronous solution" procedure describes but with even less effort from you.

пример кодаExample code

Следующий код содержит полный пример преобразования решения из синхронного в асинхронное с помощью написанного вами асинхронного метода GetURLContentsAsync.The following code contains the full example of the conversion from a synchronous to an asynchronous solution by using the asynchronous GetURLContentsAsync method that you wrote. Обратите внимание, что он очень напоминает исходное синхронное решение.Notice that it strongly resembles the original, synchronous solution.

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 +=
                $"\r\n\r\nTotal bytes returned:  {total}\r\n";
        }

        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/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/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 "https://".
            var displayURL = url.Replace("https://", "");
            resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
        }
    }
}

Следующий код содержит полный пример решения, использующего метод HttpClient, GetByteArrayAsync.The following code contains the full example of the solution that uses the HttpClient method, GetByteArrayAsync.

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 +=
                $"\r\n\r\nTotal bytes returned:  {total}\r\n";
        }

        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/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/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 "https://".
            var displayURL = url.Replace("https://", "");
            resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
        }
    }
}

См. такжеSee also