Sdílet prostřednictvím


Zrušení zbývajících asynchronních úloh po dokončení jedné (Visual Basic)

Task.WhenAny Pomocí metody společně s a CancellationTokenmůžete zrušit všechny zbývající úkoly po dokončení jednoho úkolu. Metoda WhenAny přebírá argument, který je kolekcí úkolů. Metoda spustí všechny úkoly a vrátí jeden úkol. Jeden úkol je dokončen po dokončení libovolného úkolu v kolekci.

Tento příklad ukazuje, jak použít token zrušení ve spojení s blokováním WhenAny na první úkol dokončit z kolekce úkolů a zrušit zbývající úkoly. Každý úkol stáhne obsah webu. V příkladu se zobrazí délka obsahu prvního stažení, který se dokončí, a zruší ostatní stahování.

Poznámka:

Abyste mohli spustit příklady, musíte mít na počítači nainstalovanou sadu Visual Studio 2012 nebo novější a rozhraní .NET Framework 4.5 nebo novější.

Stažení příkladu

Kompletní projekt Windows Presentation Foundation (WPF) si můžete stáhnout z asynchronní ukázky: Vyladění aplikace a pak postupujte podle těchto kroků.

  1. Dekomprimujte soubor, který jste stáhli, a spusťte Visual Studio.

  2. Na řádku nabídek zvolte Soubor, Otevřít, Projekt nebo Řešení.

  3. V dialogovém okně Otevřít projekt otevřete složku obsahující ukázkový kód, který jste dekomprimovali, a pak otevřete soubor řešení (.sln) pro AsyncFineTuningVB.

  4. V Průzkumník řešení otevřete místní nabídku projektu CancelAfterOneTask a pak zvolte Nastavit jako spouštěcí projekt.

  5. Zvolte klávesu F5, aby se projekt spustil.

    Pokud chcete projekt spustit bez ladění, zvolte klávesy Ctrl+F5.

  6. Spusťte program několikrát a ověřte, že se nejdřív dokončí jiné stahování.

Pokud si projekt nechcete stáhnout, můžete si projít MainWindow.xaml.vb soubor na konci tohoto tématu.

Sestavení příkladu

Příklad v tomto tématu se přidá do projektu vyvinutého v části Zrušit asynchronní úkol nebo seznam úkolů pro zrušení seznamu úkolů. Příklad používá stejné uživatelské rozhraní, i když se tlačítko Zrušit explicitně nepoužívá.

Pokud chcete sestavit příklad sami, postupujte krok za krokem podle pokynů v části "Stažení příkladu", ale jako počáteční projekt zvolte CancelAListOfTasks. Do tohoto projektu přidejte změny v tomto tématu.

V MainWindow.xaml.vb souboru projektu CancelAListOfTasks spusťte přechod přesunutím kroků zpracování pro každý web ze smyčky AccessTheWebAsync do následující asynchronní metody.

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

V AccessTheWebAsynctomto příkladu se používá dotaz, ToArray metoda a WhenAny metoda k vytvoření a spuštění pole úkolů. Aplikace WhenAny pole vrátí jeden úkol, který se při očekávání vyhodnotí jako první úkol, aby se dosáhlo dokončení v poli úkolů.

Proveďte následující změny v souboru AccessTheWebAsync. Hvězdičky označují změny v souboru kódu.

  1. Zakomentujte nebo odstraňte smyčku.

  2. Vytvořte dotaz, který při spuštění vytvoří kolekci obecných úloh. Každé volání, které ProcessURLAsync vrátí Task<TResult>TResult celé číslo.

    ' ***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. Volání ToArray pro spuštění dotazu a spuštění úkolů Aplikace WhenAny metody v dalším kroku by spustila dotaz a spustila úlohy bez použití ToArray, ale jiné metody nemusí. Nejbezpečnějším postupem je vynutit explicitní spuštění dotazu.

    ' ***Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Volání WhenAny kolekce úkolů WhenAny vrátí hodnotu nebo Task(Of Task(Of Integer))Task<Task<int>>. To znamená, že vrátí úkol, WhenAny který se vyhodnotí jako jeden Task(Of Integer) nebo Task<int> když se očekává. Tento jediný úkol je prvním úkolem v kolekci, který se má dokončit. Úkol, který byl dokončen jako první, je přiřazen .finishedTask Typ finishedTask je kde TResult je Task<TResult> celé číslo, protože je to návratový ProcessURLAsynctyp .

    ' ***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. V tomto příkladu vás zajímá jenom úkol, který se dokončí jako první. Proto slouží CancellationTokenSource.Cancel ke zrušení zbývajících úkolů.

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
  6. Nakonec načte finishedTask délku staženého obsahu.

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

Spusťte program několikrát a ověřte, že se nejdřív dokončí jiné stahování.

Kompletní příklad

Následující kód je úplný MainWindow.xaml.vb nebo MainWindow.xaml.cs soubor příkladu. Hvězdičky označují prvky přidané v tomto příkladu.

Všimněte si, že je nutné přidat odkaz pro System.Net.Http.

Projekt si můžete stáhnout z asynchronní ukázky: Vyladění aplikace.

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

Viz také