Condividi tramite


Avviare più attività ed elaborarle quando vengono completate (C# e Visual Basic)

Utilizzando Task.WhenAny, è possibile avviare più attività contemporaneamente ed elaborarle una ad una invece che nell'ordine in cui vengono avviate.

Nell'esempio seguente viene utilizzata una query per creare una raccolta di attività.Ogni attività scarica il contenuto di un sito Web specificato.In ogni iterazione di un ciclo while, una chiamata attesa a WhenAny restituisce l'attività nella libreria di attività che completa per prima il download.Quell'attività viene rimossa dalla raccolta ed elaborata.Il ciclo si ripete finché alla libreria non contiene attività.

[!NOTA]

Per eseguire l'esempio, è necessario disporre di Visual Studio 2012 Visual Studio express 2012 per Windows Desktop, o .NET Framework 4,5 installato nel computer.

Scaricare l'esempio

È possibile scaricare il progetto completo Windows Presentation Foundation (WPF) da Esempio di Async: Per ottimizzare l'applicazione quindi seguire questi passaggi.

  1. Decomprimere il file che viene scaricato quindi Visual Studio 2012iniziale.

  2. Sulla barra dei menu scegliere File, Apri, Progetto/Soluzione.

  3. Nella finestra di dialogo Apri progetto, aprire la cartella che contiene il codice di esempio avere decompresso quindi aprire il file di soluzione (sln) per AsyncFineTuningCS o AsyncFineTuningVB.

  4. In Esplora soluzioni, aprire il menu di scelta rapida del progetto ProcessTasksAsTheyFinish quindi scegliere Imposta come Progetto di Avvio.

  5. Scegliere il tasto F5 per eseguire il progetto.

    Scegliere i tasti CTRL+F5 per eseguire il progetto senza eseguirne il debug.

  6. Eseguire il progetto più volte per verificare che le lunghezze scaricate non siano sempre nello stesso ordine.

Se non si desidera scaricare il progetto, è possibile rivedere i file MainWindow.xaml.cs o MainWindow.xaml.vb di alla fine di questo argomento.

Compilazione dell'esempio

In questo esempio viene aggiunto al codice sviluppato in Annullare le attività rimanenti dopo che ne è stata completata una (C# e Visual Basic) e utilizza la stessa interfaccia utente.

Per compilare l'esempio manualmente, dettagliato, attenersi alle istruzioni "download la sezione" esempio, ma sceglie CancelAfterOneTask come Progetto di avvio.Aggiungere le modifiche in questo argomento al metodo di AccessTheWebAsync in tale progetto.Le modifiche sono contrassegnate con l'asterisco.

Il progetto CancelAfterOneTask include già una query che, se eseguito, crea una raccolta di attività.Ogni chiamata a ProcessURLAsync nel codice seguente restituisce un Task<TResult> dove TResult è un valore integer.

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

Nel file MainWindow.xaml.cs o MainWindow.xaml.vb del progetto, apportare le modifiche seguenti al metodo di AccessTheWebAsync.

  • Eseguire la query applicando Enumerable.ToList<TSource> anziché ToArray<TSource>.

    Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
    
    List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
    
  • Aggiungere un ciclo while che esegue i passaggi seguenti per ogni attività nella raccolta.

    1. Attesa di una chiamata a WhenAny per identificare la prima attività nella raccolta che deve completare il suo download.

      Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
      
      Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
      
    2. Rimuove quell'attività dalla raccolta.

      downloadTasks.Remove(firstFinishedTask)
      
      downloadTasks.Remove(firstFinishedTask);
      
    3. Attende firstFinishedTask, restituito da una chiamata a ProcessURLAsync.La variabile di firstFinishedTask è Task<TResult> in cui TReturn è un Integer.L'attività è già stata completata, ma è possibile metterla in attesa per recuperare la lunghezza del sito Web scaricato, come illustrato di seguito.

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

Eseguire il progetto più volte per verificare che le lunghezze scaricate non siano sempre nello stesso ordine.

Nota di avvisoAttenzione

È possibile utilizzare WhenAny in un ciclo, come descritto nell'esempio, per risolvere i problemi che includono un numero limitato di attività.Tuttavia, altri approcci sono più efficienti se si dispone di numerose attività da elaborare.Per ulteriori informazioni ed esempi, vedere Attività di elaborazione come completare.

Esempio completo

Il codice seguente è il testo completo del file MainWindow.xaml.cs o MainWindow.xaml.vb per l'esempio.Gli asterischi contrassegnano gli elementi aggiunti per questo esempio.

Si noti che è necessario aggiungere un riferimento per System.Net.Http.

È possibile scaricare il progetto da Esempio Async: Ottimizzare la vostra applicazione.

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

Vedere anche

Riferimenti

WhenAny

Concetti

Ottimizzazione dell'applicazione Async (C# e Visual Basic)

Programmazione asincrona con Async e Await (C# e Visual Basic)

Altre risorse

Async Sample: Fine Tuning Your Application