Vorgehensweise: Erweitern der asynchronen exemplarischen Vorgehensweise mit Task.WhenAll (C#)How to: Extend the async Walkthrough by Using Task.WhenAll (C#)

Sie können die Leistung der asynchronen Projektmappe in Exemplarische Vorgehensweise: Zugreifen auf das Web mit async und await (C#) verbessern, indem Sie die Task.WhenAll-Methode verwenden.You can improve the performance of the async solution in Walkthrough: Accessing the Web by Using async and await (C#) by using the Task.WhenAll method. Diese Methode wartet auf mehrere asynchrone Vorgänge, die als Auflistung von Aufgaben dargestellt werden.This method asynchronously awaits multiple asynchronous operations, which are represented as a collection of tasks.

Sie haben möglicherweise in der exemplarischen Vorgehensweise bemerkt, dass die Websites Downloads in verschiedenen Geschwindigkeiten anbieten.You might have noticed in the walkthrough that the websites download at different rates. Manchmal ist eine Website sehr langsam und verzögert alle verbleibenden Downloads.Sometimes one of the websites is very slow, which delays all the remaining downloads. Wenn Sie die asynchronen Projektmappen ausführen, die Sie in der exemplarischen Vorgehensweise erstellt haben, können Sie das Programm einfach beenden, wenn Sie nicht warten möchten. Eine bessere Option wäre jedoch, alle Downloads gleichzeitig zu starten und schnellere Downloads einfach fortfahren, ohne auf langsamere zu warten.When you run the asynchronous solutions that you build in the walkthrough, you can end the program easily if you don't want to wait, but a better option would be to start all the downloads at the same time and let faster downloads continue without waiting for the one that’s delayed.

Sie wenden die Task.WhenAll-Methode auf eine Aufgabenauflistung an.You apply the Task.WhenAll method to a collection of tasks. Die Anwendung von WhenAll gibt eine einzelne Aufgabe zurück, die nicht abgeschlossen ist, bevor jede Aufgabe in der Auflistung abgeschlossen ist.The application of WhenAll returns a single task that isn’t complete until every task in the collection is completed. Die Aufgaben scheinen parallel ausgeführt zu werden, es werden jedoch keine weiteren Threads erstellt.The tasks appear to run in parallel, but no additional threads are created. Die Aufgaben können in jeder Reihenfolge abschließen.The tasks can complete in any order.

Wichtig

Die folgenden Prozeduren beschreiben Erweiterungen zu asynchronen Anwendungen, die in Exemplarische Vorgehensweise: Zugreifen auf das Web mit async und await (C#) entwickelt werden.The following procedures describe extensions to the async applications that are developed in Walkthrough: Accessing the Web by Using async and await (C#). Sie können die Anwendungen entwickeln, indem Sie entweder die exemplarische Vorgehensweise durcharbeiten oder den Code von Codebeispiele für Entwickler herunterladen.You can develop the applications by either completing the walkthrough or downloading the code from Developer Code Samples.

Für die Ausführung des Beispiels muss Visual Studio 2012 oder höher auf dem Computer installiert sein.To run the example, you must have Visual Studio 2012 or later installed on your computer.

So fügen Sie der GetURLContentsAsync-Lösung Task.WhenAll hinzuTo add Task.WhenAll to your GetURLContentsAsync solution

  1. Fügen Sie die ProcessURLAsync-Methode der ersten Anwendung hinzu, die in Exemplarische Vorgehensweise: Zugreifen auf das Web mit async und await (C#) entwickelt wurde.Add the ProcessURLAsync method to the first application that's developed in Walkthrough: Accessing the Web by Using async and await (C#).

    • Wenn Sie die Codebeispiele für Entwickler heruntergeladen haben, öffnen Sie das „AsyncWalkthrough“-Projekt, und fügen Sie dann ProcessURLAsync der Datei „MainWindow.xaml.cs“ hinzu.If you downloaded the code from Developer Code Samples, open the AsyncWalkthrough project, and then add ProcessURLAsync to the MainWindow.xaml.cs file.

    • Wenn Sie den Code innerhalb der exemplarische Vorgehensweise entwickelt haben, fügen Sie ProcessURLAsync der Anwendung hinzu, die die GetURLContentsAsync-Methode enthält.If you developed the code by completing the walkthrough, add ProcessURLAsync to the application that includes the GetURLContentsAsync method. Die Datei „MainWindow.xaml.cs“ für diese Anwendung ist das erste Beispiel im Abschnitt „Vollständige Codebeispiele der exemplarischen Vorgehensweise“.The MainWindow.xaml.cs file for this application is the first example in the "Complete Code Examples from the Walkthrough" section.

    Die ProcessURLAsync-Methode konsolidiert die Aktionen im Text der foreach-Schleife in SumPageSizesAsync in der ersten exemplarischen Vorgehensweise.The ProcessURLAsync method consolidates the actions in the body of the foreach loop in SumPageSizesAsync in the original walkthrough. Die Methode lädt asynchron den angegebenen Inhalt einer Website als Bytearray und gibt dann die Länge des Bytearrays zurück.The method asynchronously downloads the contents of a specified website as a byte array, and then displays and returns the length of the byte array.

    private async Task<int> ProcessURLAsync(string url)  
    {  
        var byteArray = await GetURLContentsAsync(url);  
        DisplayResults(url, byteArray);  
        return byteArray.Length;  
    }  
    
  2. Kommentieren Sie die Schleife foreach in SumPageSizesAsync aus, oder löschen Sie sie, wie der folgenden Code zeigt.Comment out or delete the foreach loop in SumPageSizesAsync, as the following code shows.

    //var total = 0;  
    //foreach (var url in urlList)  
    //{  
    //    byte[] urlContents = await GetURLContentsAsync(url);  
    
    //    // The previous line abbreviates the following two assignment statements.  
    //    // GetURLContentsAsync returns a Task<T>. At completion, the task  
    //    // produces a byte array.  
    //    //Task<byte[]> getContentsTask = GetURLContentsAsync(url);  
    //    //byte[] urlContents = await getContentsTask;  
    
    //    DisplayResults(url, urlContents);  
    
    //    // Update the total.            
    //    total += urlContents.Length;  
    //}  
    
  3. Erstellen Sie eine Auflistung von Aufgaben.Create a collection of tasks. Der folgende Code definiert eine Abfrage, die beim Ausführen durch die ToArray-Methode eine Aufgabenauflistung erstellt, die die Inhalte jeder Website herunterlädt.The following code defines a query that, when executed by the ToArray method, creates a collection of tasks that download the contents of each website. Die Aufgaben werden beim Auswerten der Abfrage gestartet.The tasks are started when the query is evaluated.

    Fügen Sie nach der Deklaration von SumPageSizesAsync den folgenden Code der urlList-Methode hinzu:Add the following code to method SumPageSizesAsync after the declaration of urlList.

    // Create a query.   
    IEnumerable<Task<int>> downloadTasksQuery =   
        from url in urlList select ProcessURLAsync(url);  
    
    // Use ToArray to execute the query and start the download tasks.  
    Task<int>[] downloadTasks = downloadTasksQuery.ToArray();  
    
  4. Wenden Sie Task.WhenAll auf die Auflistung von Aufgaben downloadTasks an.Apply Task.WhenAll to the collection of tasks, downloadTasks. Task.WhenAll gibt eine einzelne abgeschlossene Aufgabe zurück, wenn alle Aufgaben in der Auflistung abgeschlossen wurden.Task.WhenAll returns a single task that finishes when all the tasks in the collection of tasks have completed.

    Im folgenden Beispiel erwartet der await-Ausdruck den Abschluss der einzelnen Aufgabe, die WhenAll zurückgibt.In the following example, the await expression awaits the completion of the single task that WhenAll returns. Der Ausdruck wird gegen ein Array mit ganzen Zahlen ausgewertet, wobei jede ganze Zahl die Länge einer heruntergeladenen Website ist.The expression evaluates to an array of integers, where each integer is the length of a downloaded website. Fügen Sie SumPageSizesAsync den folgenden Code hinzu, direkt nach dem im vorherigen Schritt hinzugefügten Code.Add the following code to SumPageSizesAsync, just after the code that you added in the previous step.

    // Await the completion of all the running tasks.  
    int[] lengths = await Task.WhenAll(downloadTasks);  
    
    //// The previous line is equivalent to the following two statements.  
    //Task<int[]> whenAllTask = Task.WhenAll(downloadTasks);  
    //int[] lengths = await whenAllTask;  
    
  5. Abschließend verwenden Sie die Sum-Methode, um die Summe der Größen aller Websites zu berechnen.Finally, use the Sum method to calculate the sum of the lengths of all the websites. Fügen Sie SumPageSizesAsync die folgende Zeile hinzu.Add the following line to SumPageSizesAsync.

    int total = lengths.Sum();  
    

So fügen Sie der HttpClient.GetByteArrayAsync-Lösung Task.WhenAll hinzuTo add Task.WhenAll to the HttpClient.GetByteArrayAsync solution

  1. Fügen Sie die folgende Version von ProcessURLAsync der zweiten Anwendung hinzu, die in Exemplarische Vorgehensweise: Zugreifen auf das Web mit async und await (C#) entwickelt wurde.Add the following version of ProcessURLAsync to the second application that's developed in Walkthrough: Accessing the Web by Using async and await (C#).

    • Wenn Sie den Code von Codebeispiele für Entwickler heruntergeladen haben, öffnen Sie das „AsyncWalkthrough_HttpClient“-Projekt und fügen dann ProcessURLAsync entweder der „MainWindow.xaml.cs“-Datei hinzu.If you downloaded the code from Developer Code Samples, open the AsyncWalkthrough_HttpClient project, and then add ProcessURLAsync to the MainWindow.xaml.cs file.

    • Wenn Sie den Code innerhalb der exemplarische Vorgehensweise entwickelt haben, fügen Sie ProcessURLAsync der Anwendung hinzu, die die HttpClient.GetByteArrayAsync-Methode verwendet.If you developed the code by completing the walkthrough, add ProcessURLAsync to the application that uses the HttpClient.GetByteArrayAsync method. Die „MainWindow.xaml.cs“-Datei für diese Anwendung ist das zweite Beispiel im Abschnitt „Vollständige Codebeispiele der exemplarischen Vorgehensweise“.The MainWindow.xaml.cs file for this application is the second example in the "Complete Code Examples from the Walkthrough" section.

    Die ProcessURLAsync-Methode konsolidiert die Aktionen im Text der foreach-Schleife in SumPageSizesAsync in der ersten exemplarischen Vorgehensweise.The ProcessURLAsync method consolidates the actions in the body of the foreach loop in SumPageSizesAsync in the original walkthrough. Die Methode lädt asynchron den angegebenen Inhalt einer Website als Bytearray und gibt dann die Länge des Bytearrays zurück.The method asynchronously downloads the contents of a specified website as a byte array, and then displays and returns the length of the byte array.

    Der einzige Unterschied der ProcessURLAsync-Methode in der vorherigen Prozedur ist die Verwendung der HttpClient-Instanz, client.The only difference from the ProcessURLAsync method in the previous procedure is the use of the HttpClient instance, client.

    async Task<int> ProcessURL(string url, HttpClient client)  
    {  
        byte[] byteArray = await client.GetByteArrayAsync(url);  
        DisplayResults(url, byteArray);  
        return byteArray.Length;  
    }  
    
  2. Kommentieren Sie die Schleife For Each oder foreach in SumPageSizesAsync aus oder löschen Sie sie, wie der folgenden Code zeigt.Comment out or delete the For Each or foreach loop in SumPageSizesAsync, as the following code shows.

    //var total = 0;  
    //foreach (var url in urlList)  
    //{  
    //    // GetByteArrayAsync returns a Task<T>. At completion, the task  
    //    // produces a byte array.  
    //    byte[] urlContent = await client.GetByteArrayAsync(url);  
    
    //    // The previous line abbreviates the following two assignment  
    //    // statements.  
    //    Task<byte[]> getContentTask = client.GetByteArrayAsync(url);  
    //    byte[] urlContent = await getContentTask;  
    
    //    DisplayResults(url, urlContent);  
    
    //    // Update the total.  
    //    total += urlContent.Length;  
    //}  
    
  3. Definieren Sie eine Abfrage, die beim Ausführen durch die ToArray-Methode eine Aufgabenauflistung erstellt, die die Inhalte jeder Website herunterlädt.Define a query that, when executed by the ToArray method, creates a collection of tasks that download the contents of each website. Die Aufgaben werden beim Auswerten der Abfrage gestartet.The tasks are started when the query is evaluated.

    Fügen Sie nach der Deklaration von SumPageSizesAsync und client den folgenden Code der urlList-Methode hinzu:Add the following code to method SumPageSizesAsync after the declaration of client and urlList.

    // Create a query.  
    IEnumerable<Task<int>> downloadTasksQuery =   
        from url in urlList select ProcessURL(url, client);  
    
    // Use ToArray to execute the query and start the download tasks.  
    Task<int>[] downloadTasks = downloadTasksQuery.ToArray();  
    
  4. Wenden Sie Task.WhenAll auf die Auflistung von Aufgaben an, downloadTasks.Next, apply Task.WhenAll to the collection of tasks, downloadTasks. Task.WhenAll gibt eine einzelne abgeschlossene Aufgabe zurück, wenn alle Aufgaben in der Auflistung abgeschlossen wurden.Task.WhenAll returns a single task that finishes when all the tasks in the collection of tasks have completed.

    Im folgenden Beispiel erwartet der await-Ausdruck den Abschluss der einzelnen Aufgabe, die WhenAll zurückgibt.In the following example, the await expression awaits the completion of the single task that WhenAll returns. Wenn vollständig, wertet der await-Ausdruck als Ergebnis ein Array von ganzen Zahlen aus, wobei jede ganze Zahl die Länge einer heruntergeladenen Website hat.When complete, the await expression evaluates to an array of integers, where each integer is the length of a downloaded website. Fügen Sie SumPageSizesAsync den folgenden Code hinzu, direkt nach dem im vorherigen Schritt hinzugefügten Code.Add the following code to SumPageSizesAsync, just after the code that you added in the previous step.

    // Await the completion of all the running tasks.  
    int[] lengths = await Task.WhenAll(downloadTasks);  
    
    //// The previous line is equivalent to the following two statements.  
    //Task<int[]> whenAllTask = Task.WhenAll(downloadTasks);  
    //int[] lengths = await whenAllTask;  
    
  5. Abschließend verwenden Sie die Sum-Methode, um die Summe der Größen aller Websites zu berechnen.Finally, use the Sum method to get the sum of the lengths of all the websites. Fügen Sie SumPageSizesAsync die folgende Zeile hinzu.Add the following line to SumPageSizesAsync.

    int total = lengths.Sum();
    

So testen Sie die Task.WhenAll-LösungenTo test the Task.WhenAll solutions

BeispielExample

Der folgende Code zeigt die Erweiterungen des Projekts, das die GetURLContentsAsync-Methode verwendet, um Inhalt aus dem Web herunterladen.The following code shows the extensions to the project that uses the GetURLContentsAsync method to download content from the web.

// Add the following using directives, and add a reference for System.Net.Http.  
using System.Net.Http;  
using System.IO;  
using System.Net;  

namespace AsyncExampleWPF_WhenAll  
{  
    public partial class MainWindow : Window  
    {  
        public MainWindow()  
        {  
            InitializeComponent();  
        }  

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

            // Two-step async call.  
            Task sumTask = SumPageSizesAsync();  
            await sumTask;  

            // One-step async call.  
            //await SumPageSizesAsync();  

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";  
        }  

        private async Task SumPageSizesAsync()  
        {  
            // Make a list of web addresses.  
            List<string> urlList = SetUpURLList();  

            // Create a query.   
            IEnumerable<Task<int>> downloadTasksQuery =   
                from url in urlList select ProcessURLAsync(url);  

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

            // You can do other work here before awaiting.  

            // Await the completion of all the running tasks.  
            int[] lengths = await Task.WhenAll(downloadTasks);  

            //// The previous line is equivalent to the following two statements.  
            //Task<int[]> whenAllTask = Task.WhenAll(downloadTasks);  
            //int[] lengths = await whenAllTask;  

            int total = lengths.Sum();  

            //var total = 0;  
            //foreach (var url in urlList)  
            //{  
            //    byte[] urlContents = await GetURLContentsAsync(url);  

            //    // The previous line abbreviates the following two assignment statements.  
            //    // GetURLContentsAsync returns a Task<T>. At completion, the task  
            //    // produces a byte array.  
            //    //Task<byte[]> getContentsTask = GetURLContentsAsync(url);  
            //    //byte[] urlContents = await getContentsTask;  

            //    DisplayResults(url, urlContents);  

            //    // Update the total.            
            //    total += urlContents.Length;  
            //}  

            // Display the total count for all of the websites.  
            resultsTextBox.Text +=  
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);  
        }  

        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/ee256749.aspx",  
                "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;  
        }  

        // The actions from the foreach loop are moved to this async method.  
        private async Task<int> ProcessURLAsync(string url)  
        {  
            var byteArray = await GetURLContentsAsync(url);  
            DisplayResults(url, byteArray);  
            return byteArray.Length;  
        }  

        private async Task<byte[]> GetURLContentsAsync(string url)  
        {  
            // The downloaded resource ends up in the variable named content.  
            var content = new MemoryStream();  

            // Initialize an HttpWebRequest for the current URL.  
            var webReq = (HttpWebRequest)WebRequest.Create(url);  

            // Send the request to the Internet resource and wait for  
            // the response.  
            using (WebResponse response = await webReq.GetResponseAsync())  
            {  
                // Get the data stream that is associated with the specified url.  
                using (Stream responseStream = response.GetResponseStream())  
                {  
                    await responseStream.CopyToAsync(content);  
                }  
            }  

            // Return the result as a byte array.  
            return content.ToArray();  

        }  

        private void DisplayResults(string url, byte[] content)  
        {  
            // Display the length of each website. The string format   
            // is designed to be used with a monospaced font, such as  
            // Lucida Console or Global Monospace.  
            var bytes = content.Length;  
            // Strip off the "http://".  
            var displayURL = url.Replace("http://", "");  
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);  

        }  
    }  
}  

BeispielExample

Der folgende Code zeigt die Erweiterungen des Projekts, das die HttpClient.GetByteArrayAsync-Methode verwendet, um Inhalt aus dem Web herunterladen.The following code shows the extensions to the project that uses method HttpClient.GetByteArrayAsync to download content from the web.

// Add the following using directives, and add a reference for System.Net.Http.  
using System.Net.Http;  
using System.IO;  
using System.Net;  

namespace AsyncExampleWPF_HttpClient_WhenAll  
{  
    public partial class MainWindow : Window  
    {  
        public MainWindow()  
        {  
            InitializeComponent();  
        }  

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

            // One-step async call.  
            await SumPageSizesAsync();  

            // Two-step async call.  
            //Task sumTask = SumPageSizesAsync();  
            //await sumTask;  

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";  
        }  

        private async Task SumPageSizesAsync()  
        {  
            // Make a list of web addresses.  
            List<string> urlList = SetUpURLList();  

            // Declare an HttpClient object and increase the buffer size. The  
            // default buffer size is 65,536.  
            HttpClient client = new HttpClient() { MaxResponseContentBufferSize = 1000000 };  

            // Create a query.  
            IEnumerable<Task<int>> downloadTasksQuery =   
                from url in urlList select ProcessURL(url, client);  

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

            // You can do other work here before awaiting.  

            // Await the completion of all the running tasks.  
            int[] lengths = await Task.WhenAll(downloadTasks);  

            //// The previous line is equivalent to the following two statements.  
            //Task<int[]> whenAllTask = Task.WhenAll(downloadTasks);  
            //int[] lengths = await whenAllTask;  

            int total = lengths.Sum();  

            //var total = 0;  
            //foreach (var url in urlList)  
            //{  
            //    // GetByteArrayAsync returns a Task<T>. At completion, the task  
            //    // produces a byte array.  
            //    byte[] urlContent = await client.GetByteArrayAsync(url);  

            //    // The previous line abbreviates the following two assignment  
            //    // statements.  
            //    Task<byte[]> getContentTask = client.GetByteArrayAsync(url);  
            //    byte[] urlContent = await getContentTask;  

            //    DisplayResults(url, urlContent);  

            //    // Update the total.  
            //    total += urlContent.Length;  
            //}  

            // Display the total count for all of the web addresses.  
            resultsTextBox.Text +=  
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);  
        }  

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

        // The actions from the foreach loop are moved to this async method.  
        async Task<int> ProcessURL(string url, HttpClient client)  
        {  
            byte[] byteArray = await client.GetByteArrayAsync(url);  
            DisplayResults(url, byteArray);  
            return byteArray.Length;  
        }  

        private void DisplayResults(string url, byte[] content)  
        {  
            // Display the length of each web site. The string format   
            // is designed to be used with a monospaced font, such as  
            // Lucida Console or Global Monospace.  
            var bytes = content.Length;  
            // Strip off the "http://".  
            var displayURL = url.Replace("http://", "");  
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);  
        }  
    }  
}  

Siehe auchSee Also

Task.WhenAll
Exemplarische Vorgehensweise: Zugreifen auf das Web mit „async“ und „await“ (C#)Walkthrough: Accessing the Web by Using async and await (C#)