Cómo: Hacer varias solicitudes web en paralelo utilizando Async y Await (C# y Visual Basic)

En un método asincrónico, se inician las tareas cuando se crean. Se aplica el operador Await (Visual Basic) o await (C#) para la tarea en el punto del método donde no se puede continuar el procesamiento hasta que la tarea termine. Con frecuencia, se espera una tarea tan pronto como se crea, como se muestra en el ejemplo siguiente.

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

Sin embargo, se puede separar la creación de la tarea de la espera de la tarea si su programa tiene otro trabajo que terminar que no depende de la finalización de la tarea.

' 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;

Entre el inicio y la espera de una tarea, se pueden iniciar otras tareas. Las tareas adicionales se ejecutarán implícitamente en paralelo, pero no se crean subprocesos adicionales.

El siguiente programa inicia tres descargas web asincrónicas y las espera en el orden en que se llaman. Observe que, al ejecutar el programa, no siempre las tareas terminan en el orden en que fueron creadas y esperadas. Empiezan a funcionar cuando se crean y una o más tareas pueden finalizar antes de que el método alcance las expresiones de espera.

Nota

Para completar este proyecto, debe tener Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 para escritorio de Windows, Visual Studio Express 2013 para Windows o .NET Framework 4.5 o 4.5.1 instalado en su equipo.

Para obtener otro ejemplo que inicie múltiples tareas al mismo tiempo, consulte Cómo: Ampliar el tutorial de Async usando Task.WhenAll (C# y Visual Basic).

Puede descargar el código de este ejemplo de Ejemplos de código para desarrolladores.

Para configurar el proyecto

  • Para configurar una aplicación WPF, complete los pasos siguientes. Puede encontrar instrucciones detalladas para realizar estos pasos en Walkthrough: Acceso a web usando Async y Await (C# y Visual Basic).

    • Cree una aplicación WPF que contenga un cuadro de texto y un botón. Nombre al botón startButton y al cuadro de texto resultsTextBox.

    • Agregue una referencia para System.Net.Http.

    • En MainWindow.xaml.vb o MainWindow.xaml.cs, agregue un fragmento Imports o una directiva using para System.Net.Http.

Para agregar el código

  1. En la ventana de diseño, MainWindow.xaml, haga doble clic en el botón para crear el controlador de eventos startButton_Click en MainWindow.xaml.vb o MainWindow.xaml.cs. Como alternativa, elija el botón, elija el icono Controladores de eventos para los elementos seleccionados en la ventana Propiedades y, a continuación, escriba startButton_Click en el cuadro de texto Clic.

  2. Copie el siguiente código y péguelo en el cuerpo de startButton_Click en MainWindow.xaml.vb o 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";
    

    El código llama a un método asincrónico, CreateMultipleTasksAsync, que controla la aplicación.

  3. Agregue los siguientes métodos de soporte técnico para el proyecto:

    • ProcessURLAsync utiliza un método HttpClient para descargar el contenido de un sitio web como una matriz de bytes. El método de soporte, ProcessURLAsync muestra seguidamente la longitud de la matriz y la devuelve.

    • DisplayResults muestra el número de bytes en la matriz de bytes para cada dirección URL. Esta pantalla muestra cuándo ha terminado de descargar cada tarea.

    Copie los siguientes métodos y péguelos después del controlador de eventos startButton_Click en MainWindow.xaml.vb o 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. Por último, defina el método CreateMultipleTasksAsync, que realiza los pasos siguientes.

    • El método declara un objeto HttpClient, que se necesita para obtener acceso al método GetByteArrayAsync en ProcessURLAsync.

    • El método crea e inicia tres tareas de tipo Task, donde TResult es un entero. A medida que cada tarea termina, DisplayResults muestra la dirección URL y la longitud del contenido descargado. Dado que las tareas se ejecutan de forma asincrónica, el orden en que los resultados aparecen podría diferir del orden en que se declararon.

    • El método espera la finalización de cada tarea. Cada operador Await o await suspende la ejecución de CreateMultipleTasksAsync hasta que termine la tarea esperada. El operador también recupera el valor devuelto de la llamada a ProcessURLAsync de cada tarea terminada.

    • Cuando se han completado las tareas y se han recuperado los valores enteros, el método suma las longitudes de los sitios web y muestra el resultado.

    Copie el siguiente método y péguelo en su solución.

    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. Elija la tecla F5 para ejecutar el programa y, a continuación, el botón Iniciar.

    Ejecute el programa varias veces para comprobar que las tres tareas no siempre terminan en el mismo orden y que el orden en que finalizan no es necesariamente el orden en que fueron creadas y esperadas.

Ejemplo

El código siguiente contiene el ejemplo completo.

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

Vea también

Tareas

Walkthrough: Acceso a web usando Async y Await (C# y Visual Basic)

Cómo: Ampliar el tutorial de Async usando Task.WhenAll (C# y Visual Basic)

Conceptos

Programación asincrónica con Async y Await (C# y Visual Basic)