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ů.
Dekomprimujte soubor, který jste stáhli, a spusťte Visual Studio.
Na řádku nabídek zvolte Soubor, Otevřít, Projekt nebo Řešení.
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.
V Průzkumník řešení otevřete místní nabídku projektu CancelAfterOneTask a pak zvolte Nastavit jako spouštěcí projekt.
Zvolte klávesu F5, aby se projekt spustil.
Pokud chcete projekt spustit bez ladění, zvolte klávesy Ctrl+F5.
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 AccessTheWebAsync
tomto 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.
Zakomentujte nebo odstraňte smyčku.
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)
Volání
ToArray
pro spuštění dotazu a spuštění úkolů AplikaceWhenAny
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()
Volání
WhenAny
kolekce úkolůWhenAny
vrátí hodnotu neboTask(Of Task(Of Integer))
Task<Task<int>>
. To znamená, že vrátí úkol,WhenAny
který se vyhodnotí jako jedenTask(Of Integer)
neboTask<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
TypfinishedTask
je kdeTResult
je Task<TResult> celé číslo, protože je to návratovýProcessURLAsync
typ .' ***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)
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()
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.