Uruchamianie wielu zadań asynchronicznych i przetwarzanie ich podczas ich wykonywania (Visual Basic)
Za pomocą programu Task.WhenAnymożna jednocześnie uruchomić wiele zadań i przetworzyć je jeden po drugim, zamiast przetwarzać je w kolejności, w której są uruchamiane.
W poniższym przykładzie użyto zapytania do utworzenia kolekcji zadań. Każde zadanie pobiera zawartość określonej witryny internetowej. W każdej iteracji pętli while oczekiwane wywołanie WhenAny
zwraca zadanie w kolekcji zadań, które najpierw kończą pobieranie. To zadanie jest usuwane z kolekcji i przetwarzane. Pętla powtarza się, dopóki kolekcja nie zawiera więcej zadań.
Uwaga
Aby uruchomić przykłady, na komputerze musi być zainstalowany program Visual Studio 2012 lub nowszy oraz program .NET Framework 4.5 lub nowszy.
Pobieranie przykładu
Pełny projekt programu Windows Presentation Foundation (WPF) można pobrać z przykładu Async: Dostosowywanie aplikacji , a następnie wykonaj następujące kroki.
Zdekompresuj pobrany plik, a następnie uruchom program Visual Studio.
Na pasku menu wybierz pozycję Plik, Otwórz, Projekt/Rozwiązanie.
W oknie dialogowym Otwieranie projektu otwórz folder zawierający zdekompresowany przykładowy kod, a następnie otwórz plik rozwiązania (.sln) dla pliku AsyncFineTuningVB.
W Eksplorator rozwiązań otwórz menu skrótów dla projektu ProcessTasksAsTheyFinish, a następnie wybierz pozycję Ustaw jako projekt startowy.
Wybierz klucz F5, aby uruchomić projekt.
Wybierz klawisze Ctrl+F5, aby uruchomić projekt bez debugowania.
Uruchom projekt kilka razy, aby sprawdzić, czy pobrane długości nie zawsze są wyświetlane w tej samej kolejności.
Jeśli nie chcesz pobierać projektu, możesz przejrzeć plik MainWindow.xaml.vb na końcu tego tematu.
Kompilowanie przykładu
W tym przykładzie dodano kod opracowany w temacie Anuluj pozostałe zadania asynchroniczne po ukończeniu jednego (Visual Basic) i używa tego samego interfejsu użytkownika.
Aby utworzyć przykład samodzielnie, postępuj zgodnie z instrukcjami w sekcji "Pobieranie przykładu", ale wybierz pozycję AnulujAfterOneTask jako projekt startowy. Dodaj zmiany w tym temacie do AccessTheWebAsync
metody w tym projekcie. Zmiany są oznaczone gwiazdkami.
Projekt CancelAfterOneTask zawiera już zapytanie, które podczas wykonywania tworzy kolekcję zadań. Każde wywołanie metody ProcessURLAsync
w poniższym kodzie zwraca liczbę Task<TResult> całkowitą.TResult
Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
From url In urlList Select ProcessURLAsync(url, client, ct)
W pliku MainWindow.xaml.vb projektu wprowadź następujące zmiany w metodzie AccessTheWebAsync
.
Wykonaj zapytanie, stosując Enumerable.ToList zamiast ToArray.
Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
Dodaj pętlę czasową, która wykonuje następujące kroki dla każdego zadania w kolekcji.
Oczekuje na wywołanie, aby
WhenAny
zidentyfikować pierwsze zadanie w kolekcji w celu zakończenia pobierania.Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
Usuwa to zadanie z kolekcji.
downloadTasks.Remove(finishedTask)
Awaits
finishedTask
, który jest zwracany przez wywołanie metodyProcessURLAsync
. ZmiennafinishedTask
Task<TResult> to gdzieTReturn
jest liczbą całkowitą. Zadanie zostało już ukończone, ale oczekujesz na pobranie długości pobranej witryny internetowej, jak pokazano w poniższym przykładzie.Dim length = Await finishedTask resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website: {0}" & vbCrLf, length)
Należy uruchomić projekt kilka razy, aby sprawdzić, czy pobrane długości nie zawsze są wyświetlane w tej samej kolejności.
Uwaga
Możesz użyć WhenAny
w pętli, zgodnie z opisem w przykładzie, aby rozwiązać problemy, które obejmują niewielką liczbę zadań. Jednak inne podejścia są bardziej wydajne, jeśli masz dużą liczbę zadań do przetworzenia. Aby uzyskać więcej informacji i przykładów, zobacz Przetwarzanie zadań po ich ukończeniu.
Kompletny przykład
Poniższy kod jest kompletnym tekstem pliku MainWindow.xaml.vb dla przykładu. Gwiazdki oznaczają elementy, które zostały dodane w tym przykładzie.
Zwróć uwagę, że należy dodać odwołanie dla elementu System.Net.Http.
Projekt można pobrać z przykładu asynchronicznego: dostrajanie aplikacji.
' 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 finishedTask 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(finishedTask)
' ***Await the first completed task and display the results.
Dim length = Await finishedTask
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/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 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.