Compartir a través de


Iniciar varias tareas y procesarlas a medida que se completan (C# y Visual Basic)

Mediante Task.WhenAny, se pueden iniciar tareas simultáneamente y procesarlas una por una a medida que se completan en lugar de procesarlas en el orden en el que se inician.

El ejemplo siguiente utiliza una consulta para crear una colección de tareas.Cada tarea descarga el contenido de un sitio web especificado.En cada iteración de un bucle while, una llamada esperada a WhenAny devuelve la tarea en la colección de tareas que finaliza primero su descarga.Esa tarea se elimina de la colección y es procesada.El bucle se repite hasta que la colección no contenga más tareas.

[!NOTA]

Para ejecutar los ejemplos, debe tener Visual Studio 2012 Visual Studio Express 2012 para el escritorio de Windows, o que.NET Framework 4,5 instalado en el equipo.

Descargar el ejemplo

Puede descargar el proyecto completado de (WPF) de Windows Presentation Foundation de Ejemplo Async: Ajustar la aplicación y después seguir estos pasos.

  1. Descomprima el archivo que ha descargado, y comienza Visual Studio 2012.

  2. En la barra de menú, elija Archivo, Abrir, Proyecto/Solución.

  3. En el cuadro de diálogo abrir proyecto, abra la carpeta que contiene el código de ejemplo que se descomprimió, y abra el archivo de solución (.sln) para AsyncFineTuningCS o AsyncFineTuningVB.

  4. En el Explorador de soluciones, abra el acceso directo del proyecto de ProcessTasksAsTheyFinish y, a continuación, elija Establecer como proyecto de inicio.

  5. Elija la tecla F5 para ejecutar el proyecto.

    Elija las teclas CTRL+F5 para ejecutar el proyecto sin depurarlo.

  6. Ejecute el proyecto varias veces para comprobar que las longitudes descargado no aparecen en el mismo orden.

Si no desea descargar el proyecto, puede revisar los archivos MainWindow.xaml.vb o MainWindow.xaml.cs al final de este tema.

Compilar el ejemplo

Este ejemplo se agrega al código que se desarrolla en Cancelar las tareas restantes cuando se completa una (C# y Visual Basic) y utiliza la misma interfaz de usuario.

Para compilar el ejemplo personalmente, gradual, siga las instrucciones de la sección “transferencia de ejemplo”, pero elija CancelAfterOneTask como proyecto de inicio.Agregue los cambios en este tema al método de AccessTheWebAsync en ese proyecto.Los cambios se marcan con asteriscos.

El proyecto CancelAfterOneTask ya incluye una consulta que, cuando se ejecuta, cree una colección de tareas.Cada llamada a ProcessURLAsync en el código siguiente devuelve Task<TResult> donde TResult es un entero.

Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
    From url In urlList Select ProcessURLAsync(url, client, ct)
IEnumerable<Task<int>> downloadTasksQuery =
    from url in urlList select ProcessURL(url, client, ct);

En el archivo MainWindow.xaml.vb o MainWindow.xaml.cs de proyecto, realice los cambios siguientes en el método de AccessTheWebAsync.

  • Ejecute la consulta por Enumerable.ToList<TSource> que se aplica en lugar de ToArray<TSource>.

    Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
    
    List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
    
  • Agregue un bucle while que realiza los pasos siguientes para cada tarea de la colección.

    1. Espera una llamada a WhenAny para identificar la primera tarea en la colección para terminar su descarga.

      Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
      
      Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
      
    2. Elimina la tarea de la colección.

      downloadTasks.Remove(firstFinishedTask)
      
      downloadTasks.Remove(firstFinishedTask);
      
    3. Espera firstFinishedTask, que es devuelta por una llamada a ProcessURLAsync.La variable de firstFinishedTask es Task<TResult> donde es un entero TReturn.La tarea ya está completa, pero se espera que obtenga la longitud del sitio web descargado, como muestra el siguiente ejemplo.

      Dim length = Await firstFinishedTask
      resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
      
      int length = await firstFinishedTask;
      resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);
      

Debe ejecutar el proyecto varias veces para comprobar que las longitudes descargado no aparecen en el mismo orden.

Nota de precauciónPrecaución

Puede utilizar WhenAny en un bucle, como se describe en el ejemplo, para solucionar problemas que implican una pequeña cantidad de tareas.Sin embargo, otros enfoques son más eficientes si tiene un gran número de tareas que procesar.Para obtener más información y ejemplos, vea Tareas de procesamiento como completa.

Ejemplo completo

El código siguiente es el texto completo del archivo MainWindow.xaml.vb o MainWindow.xaml.cs para el ejemplo.Los asteriscos marcan los elementos agregados para este ejemplo.

Observe que debe agregar una referencia a System.Net.Http.

Se puede descargar el proyecto Ejemplo asincrónico: Ajustar la aplicación.

' Add an Imports directive and a reference for System.Net.Http.
Imports System.Net.Http

' Add the following Imports directive for System.Threading.
Imports System.Threading

Class MainWindow

    ' Declare a System.Threading.CancellationTokenSource.
    Dim cts As CancellationTokenSource


    Private Async Sub startButton_Click(sender As Object, e As RoutedEventArgs)

        ' Instantiate the CancellationTokenSource.
        cts = New CancellationTokenSource()

        resultsTextBox.Clear()

        Try
            Await AccessTheWebAsync(cts.Token)
            resultsTextBox.Text &= vbCrLf & "Downloads complete."

        Catch ex As OperationCanceledException
            resultsTextBox.Text &= vbCrLf & "Downloads canceled." & vbCrLf

        Catch ex As Exception
            resultsTextBox.Text &= vbCrLf & "Downloads failed." & vbCrLf
        End Try

        ' Set the CancellationTokenSource to Nothing when the download is complete.
        cts = Nothing
    End Sub


    ' You can still include a Cancel button if you want to.
    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)

        If cts IsNot Nothing Then
            cts.Cancel()
        End If
    End Sub


    ' Provide a parameter for the CancellationToken.
    ' Change the return type to Task because the method has no return statement.
    Async Function AccessTheWebAsync(ct As CancellationToken) As Task

        Dim client As HttpClient = New HttpClient()

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

        ' ***Create a query that, when executed, returns a collection of tasks.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client, ct)

        ' ***Use ToList to execute the query and start the download tasks. 
        Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()

        ' ***Add a loop to process the tasks one at a time until none remain.
        While downloadTasks.Count > 0
            ' ***Identify the first task that completes.
            Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)

            ' ***Remove the selected task from the list so that you don't
            ' process it more than once.
            downloadTasks.Remove(firstFinishedTask)

            ' ***Await the first completed task and display the results.
            Dim length = Await firstFinishedTask
            resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
        End While

    End Function


    ' Bundle the processing steps for a website into one async method.
    Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)

        ' GetAsync returns a Task(Of HttpResponseMessage). 
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        Return urlContents.Length
    End Function


    ' Add a method that creates a list of web addresses.
    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "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

End Class


' Sample output:

' Length of the download:  226093
' Length of the download:  412588
' Length of the download:  175490
' Length of the download:  204890
' Length of the download:  158855
' Length of the download:  145790
' Length of the download:  44908
' Downloads complete.
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 a using directive and a reference for System.Net.Http.
using System.Net.Http;

// Add the following using directive.
using System.Threading;


namespace ProcessTasksAsTheyFinish
{
    public partial class MainWindow : Window
    {
        // Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;

        public MainWindow()
        {
            InitializeComponent();
        }

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

            // Instantiate the CancellationTokenSource.
            cts = new CancellationTokenSource();

            try
            {
                await AccessTheWebAsync(cts.Token);
                resultsTextBox.Text += "\r\nDownloads complete.";
            }
            catch (OperationCanceledException)
            {
                resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
            }
            catch (Exception)
            {
                resultsTextBox.Text += "\r\nDownloads failed.\r\n";
            }

            cts = null;
        }


        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }
        }


        async Task AccessTheWebAsync(CancellationToken ct)
        {
            HttpClient client = new HttpClient();

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

            // ***Create a query that, when executed, returns a collection of tasks.
            IEnumerable<Task<int>> downloadTasksQuery =
                from url in urlList select ProcessURL(url, client, ct);

            // ***Use ToList to execute the query and start the tasks. 
            List<Task<int>> downloadTasks = downloadTasksQuery.ToList();

            // ***Add a loop to process the tasks one at a time until none remain.
            while (downloadTasks.Count > 0)
            {
                    // Identify the first task that completes.
                    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);

                    // ***Remove the selected task from the list so that you don't
                    // process it more than once.
                    downloadTasks.Remove(firstFinishedTask);

                    // Await the completed task.
                    int length = await firstFinishedTask;
                    resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);
            }
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290136.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;
        }


        async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)
        {
            // GetAsync returns a Task<HttpResponseMessage>. 
            HttpResponseMessage response = await client.GetAsync(url, ct);

            // Retrieve the website contents from the HttpResponseMessage.
            byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

            return urlContents.Length;
        }
    }
}

// Sample Output:

// Length of the download:  226093
// Length of the download:  412588
// Length of the download:  175490
// Length of the download:  204890
// Length of the download:  158855
// Length of the download:  145790
// Length of the download:  44908
// Downloads complete.

Vea también

Referencia

WhenAny

Conceptos

Ajustar la aplicación de Async (C# y Visual Basic)

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

Otros recursos

Ejemplo Async: Ajustar su aplicación