Mehrere asynchrone Aufgaben starten und nach Abschluss verarbeiten (C#)Start Multiple Async Tasks and Process Them As They Complete (C#)

Mit Task.WhenAny können Sie mehrere Aufgaben gleichzeitig starten und diese nicht in der Reihenfolge, in der sie gestartet wurden, sondern zu dem Zeitpunkt, zu dem sie abgeschlossen werden, verarbeiten.By using Task.WhenAny, you can start multiple tasks at the same time and process them one by one as they’re completed rather than process them in the order in which they're started.

Im folgenden Beispiel wird eine Abfrage verwendet, um eine Auflistung von Aufgaben zu erstellen.The following example uses a query to create a collection of tasks. Jede Aufgabe lädt den Inhalt einer angegebenen Website herunter.Each task downloads the contents of a specified website. In jeder Iteration einer While-Schleife gibt ein erwarteter Aufruf von WhenAny die Aufgabe in der Auflistung von Aufgaben zurück, die ihren Download zuerst beendet.In each iteration of a while loop, an awaited call to WhenAny returns the task in the collection of tasks that finishes its download first. Diese Aufgabe wird aus der Auflistung entfernt und verarbeitet.That task is removed from the collection and processed. Die Ausführung der Schleife wird wiederholt, bis die Auflistung keine Aufgaben mehr enthält.The loop repeats until the collection contains no more tasks.

Hinweis

Zum Ausführen des Beispiels muss 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 Projekt ProcessTasksAsTheyFinish und wählen dann Als Startprojekt festlegen aus.In Solution Explorer, open the shortcut menu for the ProcessTasksAsTheyFinish 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 Projekt mehrmals aus, um zu überprüfen, dass die heruntergeladenen Längen nicht immer in der gleichen Reihenfolge angezeigt werden.Run the project several times to verify that the downloaded lengths don't always appear in the same order.

Wenn Sie das Projekt nicht herunterladen möchten, können Sie sich die Datei „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

In diesem Beispiel wird ein Code hinzugefügt, der in Verbleibende asynchrone Aufgaben nach Abschluss einer Aufgabe abbrechen (C#)Verbleibende asynchrone Aufgaben nach Abschluss einer Aufgabe abbrechen entwickelt wurde und die gleiche Benutzeroberfläche verwendet.This example adds to the code that’s developed in Cancel Remaining Async Tasks after One Is Complete (C#)Cancel Remaining Async Tasks after One Is Complete and uses the same UI.

Um das Beispiel selbst schrittweise zu erstellen, befolgen Sie die Anweisungen im Abschnitt „Herunterladen des Beispiels“. Wählen Sie als Startprojekt aber CancelAfterOneTask aus.To build the example yourself, step by step, follow the instructions in the "Downloading the Example" section, but choose CancelAfterOneTask as the StartUp Project. Fügen Sie die Änderungen in diesem Thema zur AccessTheWebAsync-Methode in diesem Projekt hinzu.Add the changes in this topic to the AccessTheWebAsync method in that project. Die Änderungen sind mit Sternchen gekennzeichnet.The changes are marked with asterisks.

Das Projekt CancelAfterOneTask enthält bereits eine Abfrage, die eine Auflistung von Aufgaben erstellt, während sie ausgeführt wird.The CancelAfterOneTask project already includes a query that, when executed, creates a collection of tasks. Jeder Aufruf von ProcessURLAsync im folgenden Code gibt Task<TResult> zurück, wobei TResult eine ganze Zahl ist.Each call to ProcessURLAsync in the following code returns a Task<TResult> where TResult is an integer.

IEnumerable<Task<int>> downloadTasksQuery =  
    from url in urlList select ProcessURL(url, client, ct);  

Nehmen Sie in der Datei „MainWindow.xaml.cs“ des Projekts die folgenden Änderungen an der AccessTheWebAsync-Methode vor.In the MainWindow.xaml.cs file of the project, make the following changes to the AccessTheWebAsync method.

  • Führen Sie die Abfrage aus, indem Sie Enumerable.ToList anstelle von ToArray anwenden.Execute the query by applying Enumerable.ToList instead of ToArray.

    List<Task<int>> downloadTasks = downloadTasksQuery.ToList();  
    
  • Fügen Sie eine While-Schleife hinzu, die die folgenden Schritte für jede Aufgabe in der Auflistung ausführt.Add a while loop that performs the following steps for each task in the collection.

    1. Erwartet einen Aufruf von WhenAny, um die erste Aufgabe in der Auflistung zu identifizieren, die ihren Download beendet.Awaits a call to WhenAny to identify the first task in the collection to finish its download.

      Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);  
      
    2. Entfernt die entsprechende Aufgabe aus der Auflistung.Removes that task from the collection.

      downloadTasks.Remove(firstFinishedTask);  
      
    3. Erwartet firstFinishedTask, das durch einen Aufruf von ProcessURLAsync zurückgegeben wird.Awaits firstFinishedTask, which is returned by a call to ProcessURLAsync. Die Variable firstFinishedTask ist eine Task<TResult>, wobei TReturn eine ganze Zahl ist.The firstFinishedTask variable is a Task<TResult> where TReturn is an integer. Die Aufgabe ist bereits abgeschlossen, aber es darauf gewartet, dass von ihr die Länge der heruntergeladenen Website abgerufen wird, wie im folgenden Beispiel dargestellt.The task is already complete, but you await it to retrieve the length of the downloaded website, as the following example shows.

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

Sie sollten das Projekt mehrmals ausführen, um zu überprüfen, dass die heruntergeladenen Längen nicht immer in der gleichen Reihenfolge angezeigt werden.You should run the project several times to verify that the downloaded lengths don't always appear in the same order.

Achtung

Sie können WhenAny in einer Schleife gemäß der Beschreibung im Beispiel verwenden, um Problemlösungen zu entwickeln, die nur wenige Aufgaben umfassen.You can use WhenAny in a loop, as described in the example, to solve problems that involve a small number of tasks. Andere Ansätze sind jedoch effizienter, wenn viele Aufgaben verarbeitet werden müssen.However, other approaches are more efficient if you have a large number of tasks to process. Weitere Informationen und Beispiele finden Sie unter Processing Tasks as they complete (Verarbeitung von Aufgaben nach deren Abschluss).For more information and examples, see Processing Tasks as they complete.

Vollständiges BeispielComplete Example

Der folgende Code besteht aus dem vollständigen Text der Datei „MainWindow.xaml.cs“ für das Beispiel.The following code is the complete text of the 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 ProcessTasksAsTheyFinish  
{  
    public partial class MainWindow : Window  
    {  
        // Declare a System.Threading.CancellationTokenSource.  
        CancellationTokenSource cts;  

        public MainWindow()  
        {  
            InitializeComponent();  
        }  

        private async void startButton_Click(object sender, RoutedEventArgs e)  
        {  
            resultsTextBox.Clear();  

            // Instantiate the CancellationTokenSource.  
            cts = new CancellationTokenSource();  

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

            cts = null;  
        }  

        private void cancelButton_Click(object sender, RoutedEventArgs e)  
        {  
            if (cts != null)  
            {  
                cts.Cancel();  
            }  
        }  

        async Task AccessTheWebAsync(CancellationToken ct)  
        {  
            HttpClient client = new HttpClient();  

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

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

            // ***Use ToList to execute the query and start the tasks.   
            List<Task<int>> downloadTasks = 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.  
                    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);  

                    // ***Remove the selected task from the list so that you don't  
                    // process it more than once.  
                    downloadTasks.Remove(firstFinishedTask);  

                    // Await the completed task.  
                    int length = await firstFinishedTask;  
                    resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);  
            }  
        }  

        private List<string> SetUpURLList()  
        {  
            List<string> urls = new List<string>   
            {   
                "http://msdn.microsoft.com",  
                "http://msdn.microsoft.com/library/windows/apps/br211380.aspx",  
                "http://msdn.microsoft.com/library/hh290136.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;  
        }  

        async Task<int> ProcessURL(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;  
        }  
    }  
}  

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

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