Udostępnij za pośrednictwem


Anulowanie pozostałych zadań asynchronicznych po zakończeniu jednego (C# i Visual Basic)

Za pomocą metody Task.WhenAny wraz z CancellationToken, możesz anulować wszystkie pozostałe zadania po wykonaniu jednego zadania.Metoda WhenAny przyjmuje argument, który jest kolekcją zadań.Metoda uruchamia wszystkie zadania i zwraca jedno zadanie.Pojedyncze zadanie jest ukończone, gdy zostanie ukończone dowolne zadanie w kolekcji.

W tym przykładzie przedstawiono sposób użycia tokenu anulowania w połączeniu z metodą WhenAny do zatrzymania się na pierwszym zadaniu do ukończenia i anulowania pozostałych zadań.Każde zadanie powoduje pobieranie zawartości witryny sieci Web.Przykład wyświetla długość zawartości pierwszego pobieranego pliku i anuluje inne pliki do pobrania.

[!UWAGA]

Aby uruchomić przykłady, na komputerze musisz mieć zainstalowane programy Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows lub .NET Framework w wersji 4.5 lub 4.5.1.

Pobieranie przykładu

Można pobrać pełny projekt Windows Presentation Foundation (WPF) z Próbka asynchroniczna: Dostrajanie aplikacji, a następnie należy wykonać poniższe kroki.

  1. Przeprowadź dekompresję pobranego pliku, a następnie uruchom Visual Studio.

  2. Na pasku menu wybierz kolejno opcje Plik, Otwórz i Projekt/rozwiązanie.

  3. W oknie dialogowym Otwórz projekt, otwórz folder, który zawiera przykładowy kod, który został zdekompresowany, a następnie otwórz plik rozwiązania (.sln) dla AsyncFineTuningCS lub AsyncFineTuningVB.

  4. W Eksploratorze rozwiązań otwórz menu skrótów dla projektu CancelAfterOneTask, a następnie wybierz polecenie Ustaw jako projekt startowy.

  5. Wybierz klawisz F5, aby uruchomić aplikację.

    Wybierz klawisze Ctrl + F5, aby uruchomić projekt bez debugowania go.

  6. Uruchom program kilka razy, aby sprawdzić, czy najpierw kończą się różne pobrania.

Jeśli nie chcesz pobrać projektu, możesz przejrzeć pliki MainWindow.xaml.vb i MainWindow.xaml.cs na końcu tego tematu.

Budowanie przykładu

Przykład w tym temacie zostaje stanowi dodatek do projektu opracowanego w artykule Anulowanie zadania asynchronicznego lub listy zadań (C# i Visual Basic) umożliwiający anulowanie listy zadań.W przykładzie użyto tego samego interfejsu użytkownika, chociaż przycisk Anuluj nie jest używany jawnie.

Aby zbudować przykład samodzielnie, krok po kroku, postępuj zgodnie z instrukcjami podanymi w części Pobieranie przykładu, ale wybierając CancelAListOfTasks jako Projekt startowy.Dodaj zmiany w tym temacie do tego projektu.

W pliku MainWindow.xaml.vb lub MainWindow.xaml.cs CancelAListOfTasks projektu, uruchom przejścia przez przeniesienie kroków przetwarzania dla każdej witryny sieci Web z pętli w AccessTheWebAsync według metody asynchronicznej.

' ***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
// ***Bundle the processing steps for a website into one async method.
async Task<int> ProcessURLAsync(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;
}

W programie AccessTheWebAsync w tym przykładzie użyto zapytania, metody ToArray``1 i metody WhenAny, aby utworzyć i uruchomić szereg zadań.Zastosowanie metody WhenAny do tablicy powoduje zwrócenie pojedynczego zadania, które, gdy jest oczekiwane, daje w wyniku pierwsze zadanie do wykonania w tablicy zadań.

Należy wprowadzić następujące zmiany w AccessTheWebAsync.Gwiazdka oznacza zmiany w pliku kodu.

  1. Skomentuj lub usuń pętlę.

  2. Utwórz zapytanie, które po wykonaniu, daje kolekcję zadań rodzajowych.Każde wywołanie metody ProcessURLAsync zwraca element Task, gdzie TResult jest liczbą całkowitą.

    ' ***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)
    
    // ***Create a query that, when executed, returns a collection of tasks.
    IEnumerable<Task<int>> downloadTasksQuery =
        from url in urlList select ProcessURLAsync(url, client, ct);
    
  3. Wywołaj ToArray, aby wykonać zapytanie i uruchomić zadania.Zastosowanie metody WhenAny w następnym kroku spowoduje wykonanie zapytania i uruchomienie zadań bez używania metody ToArray, ale niekoniecznie innych metod.Najbezpieczniejszym rozwiązaniem jest jawne wymuszenie wykonania zapytania.

    ' ***Use ToArray to execute the query and start the download tasks.  
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
    // ***Use ToArray to execute the query and start the download tasks. 
    Task<int>[] downloadTasks = downloadTasksQuery.ToArray();
    
  4. Wywołaj WhenAny na kolekcji zadań.WhenAny zwraca Task(Of Task(Of Integer)) lub Task<Task<int>>. Oznacza to, że metoda WhenAny zwraca zadanie, którego wynikiem jest jeden obiekt Task(Of Integer) lub Task<int>, gdy jest oczekiwane.To pojedyncze zadanie jest pierwszym zadaniem w kolekcji, które ma zostać zakończone.Zadanie, które kończy się jako pierwsze, zostaje przypisane do zmiennej firstFinishedTask.Typem zmiennej firstFinishedTask jest Task, gdzie TResult jest liczbą całkowitą, ponieważ jest to typ zwracany metody ProcessURLAsync.

    ' ***Call WhenAny and then await the result. The task that finishes  
    ' first is assigned to firstFinishedTask. 
    Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
    
    // ***Call WhenAny and then await the result. The task that finishes  
    // first is assigned to firstFinishedTask.
    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
    
  5. W tym przykładzie interesuje Cię tylko zadanie, które zakończy się po raz pierwszy.W związku z tym użyj metody CancellationTokenSource.Cancel, aby anulować pozostałe zadania.

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
    // ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel();
    
  6. Na koniec poczekaj na wartość firstFinishedTask, aby odczytać długość pobranej zawartości.

    Dim length = Await firstFinishedTask
    resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
    
    var length = await firstFinishedTask;
    resultsTextBox.Text += String.Format("\r\nLength of the downloaded website:  {0}\r\n", length);
    

Uruchom program kilka razy, aby sprawdzić, czy najpierw kończą się różne pobrania.

Kompletny przykład

Poniższy kod jest pełnym plikiem MainWindow.xaml.vb lub MainWindow.xaml.cs dla przykładu.Gwiazdki oznaczają elementy, które zostały dodane dla tego przykładu.

Należy zauważyć, że należy dodać odwołanie do System.Net.Http.

Można ściągnąć projekt z Async Sample: Fine Tuning Your Application.

' 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 &= 
        ''        String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, urlContents.Length) 
        ''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 firstFinishedTask. 
        Dim firstFinishedTask 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 firstFinishedTask
        resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
    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 downloaded website:  158856 

' Download 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 CancelAfterOneTask
{
    public partial class MainWindow : Window
    {
        // Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;

        public MainWindow()
        {
            InitializeComponent();
        }


        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            // Instantiate the CancellationTokenSource.
            cts = new CancellationTokenSource();

            resultsTextBox.Clear();

            try
            {
                await AccessTheWebAsync(cts.Token);
                resultsTextBox.Text += "\r\nDownload complete.";
            }
            catch (OperationCanceledException)
            {
                resultsTextBox.Text += "\r\nDownload canceled.";
            }
            catch (Exception)
            {
                resultsTextBox.Text += "\r\nDownload failed.";
            }

            // Set the CancellationTokenSource to null when the download is complete.
            cts = null;
        }


        // You can still include a Cancel button if you want to. 
        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }
        }


        // Provide a parameter for the CancellationToken.
        async Task AccessTheWebAsync(CancellationToken ct)
        {
            HttpClient client = new HttpClient();

            // Call SetUpURLList to make a list of web addresses.
            List<string> urlList = SetUpURLList();

            // ***Comment out or delete the loop. 
            //foreach (var url in urlList) 
            //{ 
            //    // GetAsync returns a Task<HttpResponseMessage>.  
            //    // Argument ct carries the message if the Cancel button is chosen.  
            //    // ***Note that the Cancel button can cancel all remaining downloads. 
            //    HttpResponseMessage response = await client.GetAsync(url, ct); 

            //    // Retrieve the website contents from the HttpResponseMessage. 
            //    byte[] urlContents = await response.Content.ReadAsByteArrayAsync(); 

            //    resultsTextBox.Text += 
            //        String.Format("\r\nLength of the downloaded string: {0}.\r\n", urlContents.Length);
            //} 

            // ***Create a query that, when executed, returns a collection of tasks.
            IEnumerable<Task<int>> downloadTasksQuery =
                from url in urlList select ProcessURLAsync(url, client, ct);

            // ***Use ToArray to execute the query and start the download tasks. 
            Task<int>[] downloadTasks = downloadTasksQuery.ToArray();

            // ***Call WhenAny and then await the result. The task that finishes  
            // first is assigned to firstFinishedTask.
            Task<int> firstFinishedTask = 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. 
            var length = await firstFinishedTask;
            resultsTextBox.Text += String.Format("\r\nLength of the downloaded website:  {0}\r\n", length);
        }


        // ***Bundle the processing steps for a website into one async method.
        async Task<int> ProcessURLAsync(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;
        }


        // Add a method that creates a list of web addresses. 
        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "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;
        }
    }
    // Sample output: 

    // Length of the downloaded website:  158856 

    // Download complete.
}

Zobacz też

Informacje

WhenAny

Koncepcje

Dostrajanie aplikacji Async (C# i Visual Basic)

Programowanie asynchroniczne z Async i Await (C# i Visual Basic)

Inne zasoby

Próbka asynchroniczna: Dostrajanie aplikacji