Condividi tramite


Annullare le attività asincrone rimanenti dopo il completamento di un'attività (Visual Basic)

Usando il metodo Task.WhenAny insieme a CancellationToken, è possibile annullare tutte le attività rimanenti quando una attività è completata. Il WhenAny metodo accetta un argomento che è una raccolta di attività. Il metodo avvia tutte le attività e restituisce una singola attività. L'attività singola viene considerata completata quando una qualsiasi attività nella raccolta è completata.

In questo esempio viene illustrato come usare un token di annullamento insieme a WhenAny per individuare la prima attività completata dalla raccolta di attività e per annullare le attività rimanenti. Ogni attività scarica il contenuto di un sito Web. Nell'esempio viene visualizzata la lunghezza del contenuto del primo download per completare e annullare gli altri download.

Annotazioni

Per eseguire gli esempi, è necessario che Nel computer sia installato Visual Studio 2012 o versione successiva e .NET Framework 4.5 o versione successiva.

Download dell'esempio

È possibile scaricare il progetto Windows Presentation Foundation (WPF) completo da Esempio asincrono: Ottimizzazione dell'applicazione e quindi seguire questa procedura.

  1. Decomprimere il file scaricato e quindi avviare Visual Studio.

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

  3. Nella finestra di dialogo Apri progetto aprire la cartella contenente il codice di esempio decompresso e quindi aprire il file della soluzione (.sln) per AsyncFineTuningVB.

  4. In Esplora soluzioni aprire il menu di scelta rapida per il progetto CancelAfterOneTask e 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 programma più volte per verificare quale download diverso finisca per primo.

Se non si vuole scaricare il progetto, è possibile esaminare il file di MainWindow.xaml.vb alla fine di questo argomento.

Compilazione dell'esempio

Nell'esempio riportato in questo argomento, si fa riferimento al progetto sviluppato in Annullare un'attività asincrona o un elenco di attività per mostrare come annullare un elenco di attività. L'esempio usa la stessa interfaccia utente, anche se il pulsante Annulla non viene usato in modo esplicito.

Per costruire passo passo l'esempio da soli, seguire le istruzioni nella sezione "Scaricamento dell'Esempio", ma scegliere CancelAListOfTasks come progetto di avvio. Aggiungi le modifiche di questo argomento a quel progetto.

Nel file MainWindow.xaml.vb del progetto CancelAListOfTasks, inizia la transizione spostando i passaggi di elaborazione per ogni sito Web dal ciclo indicato in AccessTheWebAsync al metodo asincrono seguente.

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

In AccessTheWebAsyncquesto esempio viene usata una query, il ToArray metodo e il WhenAny metodo per creare e avviare una matrice di attività. L'applicazione di WhenAny all'array restituisce una singola attività che, quando attesa, restituisce la prima attività che viene completata nell'array di attività.

Apportare le modifiche seguenti in AccessTheWebAsync. Gli asterischi contrassegnano le modifiche nel file di codice.

  1. Metti come commento o elimina il ciclo.

  2. Creare una query che, quando eseguita, produce una raccolta di attività generica. Ogni chiamata a ProcessURLAsync restituisce un Task<TResult> in cui TResult è un numero intero.

    ' ***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)
    
  3. Chiamare ToArray per eseguire la query e avviare le attività. L'applicazione del metodo WhenAny nel passaggio successivo eseguirebbe la query e avvierebbe le attività senza usare ToArray, mentre altri metodi potrebbero non farlo. La procedura più sicura consiste nel forzare l'esecuzione della query in modo esplicito.

    ' ***Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Chiamare WhenAny nella raccolta di attività. WhenAny restituisce un Task(Of Task(Of Integer)) o Task<Task<int>>. Ovvero, WhenAny restituisce un task che valuta a un singolo Task(Of Integer) o Task<int> quando è in attesa di completamento. Questa singola attività è la prima attività della raccolta da completare. L'attività completata per prima viene assegnata a finishedTask. Il tipo di finishedTask è Task<TResult> dove TResult è un numero intero perché è il tipo restituito di ProcessURLAsync.

    ' ***Call WhenAny and then await the result. The task that finishes
    ' first is assigned to finishedTask.
    Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
    
  5. In questo esempio si è interessati solo all'attività che termina per prima. Pertanto, usare CancellationTokenSource.Cancel per annullare le attività rimanenti.

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
  6. Infine, utilizza il metodo await finishedTask per recuperare la lunghezza del contenuto scaricato.

    Dim length = Await finishedTask
    resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website:  {length}" & vbCrLf
    

Eseguire il programma più volte per verificare quale download diverso finisca per primo.

Esempio completo

Il codice seguente è il file completo MainWindow.xaml.vb o MainWindow.xaml.cs 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 asincrono: Ottimizzazione dell'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 & "Download complete."

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

        Catch ex As Exception
            resultsTextBox.Text &= vbCrLf & "Download 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()

        '' Comment out or delete the loop.
        ''For Each url In urlList
        ''    ' GetAsync returns a Task(Of HttpResponseMessage).
        ''    ' Argument ct carries the message if the Cancel button is chosen.
        ''    ' Note that the Cancel button can cancel all remaining downloads.
        ''    Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

        ''    resultsTextBox.Text &=
        ''        vbCrLf & $"Length of the downloaded string: {urlContents.Length}." & vbCrLf
        ''Next

        ' ***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 ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' ***Call WhenAny and then await the result. The task that finishes
        ' first is assigned to finishedTask.
        Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)

        ' ***Cancel the rest of the downloads. You just want the first one.
        cts.Cancel()

        ' ***Await the first completed task and display the results
        ' Run the program several times to demonstrate that different
        ' websites can finish first.
        Dim length = Await finishedTask
        resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website:  {length}" & vbCrLf
    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/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
    End Function

End Class

' Sample output:

' Length of the downloaded website:  158856

' Download complete.

Vedere anche