Verbleibende asynchrone Aufgaben nach Abschluss einer Aufgabe abbrechen (C#)Cancel Remaining Async Tasks after One Is Complete (C#)

Mit der Task.WhenAny-Methode zusammen mit einem CancellationToken können Sie alle verbleibenden Aufgaben abbrechen, wenn eine Aufgabe abgeschlossen wurde.By using the Task.WhenAny method together with a CancellationToken, you can cancel all remaining tasks when one task is complete. Die WhenAny-Methode akzeptiert ein Argument, das eine Auflistung von Aufgaben ist.The WhenAny method takes an argument that’s a collection of tasks. Die Methode startet alle Aufgaben und gibt eine einzelne Aufgabe zurück.The method starts all the tasks and returns a single task. Die einzelne Aufgabe ist abgeschlossen, wenn eine beliebige Aufgabe in der Auflistung abgeschlossen ist.The single task is complete when any task in the collection is complete.

In diesem Beispiel wird veranschaulicht, wie ein Abbruchtoken in Verbindung mit WhenAny verwendet wird, um an der ersten Aufgabe festzuhalten, die in der Auflistung von Aufgaben beendet wird, und die übrigen Aufgaben abzubrechen.This example demonstrates how to use a cancellation token in conjunction with WhenAny to hold onto the first task to finish from the collection of tasks and to cancel the remaining tasks. Jede Aufgabe lädt den Inhalt einer Website herunter.Each task downloads the contents of a website. Im Beispiel wird die Länge des Inhalts des ersten abgeschlossenen Downloads angezeigt und die anderen Downloads abgebrochen.The example displays the length of the contents of the first download to complete and cancels the other downloads.

Hinweis

Zum Ausführen der Beispiele müssen Visual Studio 2012 oder höher sowie .NET Framework 4.5 oder höher auf dem Computer installiert sein.To run the examples, you must have Visual Studio 2012 or newer and the .NET Framework 4.5 or newer installed on your computer.

Herunterladen des BeispielsDownloading the Example

Sie können das vollständige Windows Presentation Foundation (WPF)-Projekt von Async Sample: Fine Tuning Your Application herunterladen und anschließend die folgenden Schritte ausführen.You can download the complete Windows Presentation Foundation (WPF) project from Async Sample: Fine Tuning Your Application and then follow these steps.

  1. Dekomprimieren Sie die heruntergeladene Datei, und starten Sie dann Visual Studio.Decompress the file that you downloaded, and then start Visual Studio.

  2. Klicken Sie in der Menüleiste auf Datei, dann auf Öffnenund Projekt/Projektmappe.On the menu bar, choose File, Open, Project/Solution.

  3. Öffnen Sie im Dialogfeld Projekt öffnen den Ordner, der den von Ihnen dekomprimierten Beispielcode enthält, und öffnen Sie anschließend die Projektmappendatei (SLN-Datei) für AsyncFineTuningCS.In the Open Project dialog box, open the folder that holds the sample code that you decompressed, and then open the solution (.sln) file for AsyncFineTuningCS.

  4. Öffnen Sie im Projektmappen-Explorer das Kontextmenü für das CancelAfterOneTask-Projekt, und wählen Sie dann Als Startprojekt festlegen aus.In Solution Explorer, open the shortcut menu for the CancelAfterOneTask project, and then choose Set as StartUp Project.

  5. Drücken Sie die Taste F5, um das Projekt auszuführen.Choose the F5 key to run the project.

    Drücken Sie STRG+F5, um das Projekt auszuführen, ohne es zu debuggen.Choose the Ctrl+F5 keys to run the project without debugging it.

  6. Führen Sie das Programm mehrmals aus, um zu überprüfen, dass unterschiedliche Downloads als erste beendet werden.Run the program several times to verify that different downloads finish first.

Wenn Sie das Projekt nicht herunterladen möchten, können Sie sich die Dateien „MainWindow.xaml.vb“ und „MainWindow.xaml.cs“ am Ende dieses Themas anschauen.If you don't want to download the project, you can review the MainWindow.xaml.cs file at the end of this topic.

Erstellen des BeispielsBuilding the Example

Das Beispiel in diesem Thema baut auf dem Projekt auf, das unter Eine asynchrone Aufgabe oder Aufgabenliste abbrechen (C#) entwickelt wurde, um eine Aufgabenliste abzubrechen.The example in this topic adds to the project that's developed in Cancel an Async Task or a List of Tasks (C#) to cancel a list of tasks. Im Beispiel wird die gleiche UI verwendet, obwohl die Schaltfläche Abbrechen nicht explizit verwendet wird.The example uses the same UI, although the Cancel button isn’t used explicitly.

Um das Beispiel selbst schrittweise zu erstellen, befolgen Sie die Anweisungen im Abschnitt „Herunterladen des Beispiels“. Wählen Sie als Startprojekt aber CancelAListOfTasks aus.To build the example yourself, step by step, follow the instructions in the "Downloading the Example" section, but choose CancelAListOfTasks as the StartUp Project. Fügen Sie diesem Projekt die Änderungen in diesem Thema hinzu.Add the changes in this topic to that project.

Starten Sie in der Datei „MainWindow.xaml.cs“ des CancelAListOfTasks-Projekts den Übergang, indem Sie die Verarbeitungsschritte für jede Website von der Schleife in AccessTheWebAsync zur folgenden asynchronen Methode verschieben.In the MainWindow.xaml.cs file of the CancelAListOfTasks project, start the transition by moving the processing steps for each website from the loop in AccessTheWebAsync to the following async method.

/ ***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;  
}  

In AccessTheWebAsync wird in diesem Beispiel eine Abfrage, die ToArray-Methode und die WhenAny-Methode verwendet, um ein Array von Aufgaben zu erstellen und zu starten.In AccessTheWebAsync, this example uses a query, the ToArray method, and the WhenAny method to create and start an array of tasks. Die Anwendung von WhenAny auf den Array gibt eine einzelne Aufgabe zurück, die, wenn sie erwartet wird, zur ersten Aufgabe auswertet wird, die im Array von Aufgaben zum Abschluss kommt.The application of WhenAny to the array returns a single task that, when awaited, evaluates to the first task to reach completion in the array of tasks.

Nehmen Sie in AccessTheWebAsync die folgenden Änderungen vor.Make the following changes in AccessTheWebAsync. Die Änderungen in der Codedatei sind mit Sternchen gekennzeichnet.Asterisks mark the changes in the code file.

  1. Kommentieren Sie die Schleife aus oder löschen Sie sie.Comment out or delete the loop.

  2. Erstellen Sie eine Abfrage, die eine Auflistung generischer Aufgaben erstellt, wenn sie ausgeführt wird.Create a query that, when executed, produces a collection of generic tasks. Jeder Aufruf von ProcessURLAsync gibt Task<TResult> zurück, wobei TResult eine ganze Zahl ist.Each call to ProcessURLAsync returns a Task<TResult> where TResult is an integer.

    // ***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. Rufen Sie ToArray auf, um die Abfrage auszuführen und die Aufgaben zu starten.Call ToArray to execute the query and start the tasks. Die Anwendung der WhenAny-Methode im nächsten Schritt würde die Abfrage ohne ToArray ausführen und die Aufgaben starten, bei anderen Methoden ist dies möglicherweise nicht der Fall.The application of the WhenAny method in the next step would execute the query and start the tasks without using ToArray, but other methods might not. Die sicherste Methode besteht darin, die Ausführung der Abfrage explizit zu erzwingen.The safest practice is to force execution of the query explicitly.

    // ***Use ToArray to execute the query and start the download tasks.   
    Task<int>[] downloadTasks = downloadTasksQuery.ToArray();  
    
  4. Rufen Sie WhenAny mit der Auflistung von Aufgaben auf.Call WhenAny on the collection of tasks. WhenAny gibt Task(Of Task(Of Integer)) oder Task<Task<int>> zurück.WhenAny returns a Task(Of Task(Of Integer)) or Task<Task<int>>. Das bedeutet, dass WhenAny eine Aufgabe zurückgibt, die zu einem einzelnen Task(Of Integer) oder Task<int> ausgewertet wird, wenn sie erwartet wird.That is, WhenAny returns a task that evaluates to a single Task(Of Integer) or Task<int> when it’s awaited. Diese einzelne Aufgabe ist die erste Aufgabe in der Auflistung, die beendet wird.That single task is the first task in the collection to finish. Die Aufgabe, die als erste beendet wird, wird firstFinishedTask zugewiesen.The task that finished first is assigned to firstFinishedTask. Der Typ von firstFinishedTask ist Task<TResult>, wobei TResult eine ganze Zahl ist, da dies der Rückgabetyp von ProcessURLAsync ist.The type of firstFinishedTask is Task<TResult> where TResult is an integer because that's the return type of ProcessURLAsync.

    // ***Call WhenAny and then await the result. The task that finishes   
    // first is assigned to firstFinishedTask.  
    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);  
    
  5. In diesem Beispiel sind Sie nur an der Aufgabe interessiert, die zuerst beendet wird.In this example, you’re interested only in the task that finishes first. Verwenden Sie daher CancellationTokenSource.Cancel, um die verbleibenden Aufgaben abzubrechen.Therefore, use CancellationTokenSource.Cancel to cancel the remaining tasks.

    // ***Cancel the rest of the downloads. You just want the first one.  
    cts.Cancel();  
    
  6. Schließlich warten Sie ab, dass firstFinishedTask die Länge des heruntergeladenen Inhalts abruft.Finally, await firstFinishedTask to retrieve the length of the downloaded content.

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

Führen Sie das Programm mehrmals aus, um zu überprüfen, dass unterschiedliche Downloads als erste beendet werden.Run the program several times to verify that different downloads finish first.

Vollständiges BeispielComplete Example

Der folgende Code besteht aus der vollständigen Datei „MainWindow.xaml.cs“ für das Beispiel.The following code is the complete MainWindow.xaml.cs file for the example. Sternchen markieren die Elemente, die für dieses Beispiel hinzugefügt wurden.Asterisks mark the elements that were added for this example.

Beachten Sie, dass Sie einen Verweis für System.Net.Http hinzufügen müssen.Notice that you must add a reference for System.Net.Http.

Sie können das Projekt von Async Sample: Fine Tuning Your Application herunterladen.You can download the project from Async Sample: Fine Tuning Your Application.

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>   
            {   
                "http://msdn.microsoft.com",  
                "http://msdn.microsoft.com/library/hh290138.aspx",  
                "http://msdn.microsoft.com/library/hh290140.aspx",  
                "http://msdn.microsoft.com/library/dd470362.aspx",  
                "http://msdn.microsoft.com/library/aa578028.aspx",  
                "http://msdn.microsoft.com/library/ms404677.aspx",  
                "http://msdn.microsoft.com/library/ff730837.aspx"  
            };  
            return urls;  
        }  
    }  
    // Sample output:  

    // Length of the downloaded website:  158856  

    // Download complete.  
}  

Siehe auchSee Also

WhenAny
Feinabstimmung der Async-Anwendung (C#)Fine-Tuning Your Async Application (C#)
Asynchronous Programming with async and await (C#) (Asynchrone Programmierung mit Async und Await (C#))Asynchronous Programming with async and await (C#)
Async Sample: Fine Tuning Your Application (Async-Beispiel: Feinabstimmung der Anwendung)Async Sample: Fine Tuning Your Application