How to: Make Multiple Web Requests in Parallel by Using Async and Await (C# and Visual Basic)

In an async method, tasks are started when they’re created. The Await (Visual Basic) or await (C#) operator is applied to the task at the point in the method where processing can’t continue until the task finishes. Often a task is awaited as soon as it’s created, as the following example shows.

var result = await someWebAccessMethodAsync(url);

However, you can separate creating the task from awaiting the task if your program has other work to accomplish that doesn’t depend on the completion of the task.

// The following line creates and starts the task.
var myTask = someWebAccessMethodAsync(url);

// While the task is running, you can do other work that doesn't depend
// on the results of the task.
// . . . . .

// The application of await suspends the rest of this method until the task is complete.
var result = await myTask;

Between starting a task and awaiting it, you can start other tasks. The additional tasks implicitly run in parallel, but no additional threads are created.

The following program starts three asynchronous web downloads and then awaits them in the order in which they’re called. Notice, when you run the program, that the tasks don’t always finish in the order in which they’re created and awaited. They start to run when they’re created, and one or more of the tasks might finish before the method reaches the await expressions.

NoteNote

To complete this project, you must have Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows Desktop, Visual Studio Express 2013 for Windows, or the .NET Framework 4.5 or 4.5.1 installed on your computer.

For another example that starts multiple tasks at the same time, see How to: Extend the Async Walkthrough by Using Task.WhenAll (C# and Visual Basic).

You can download the code for this example from Developer Code Samples.

To set up the project

  • To set up a WPF application, complete the following steps. You can find detailed instructions for these steps in Walkthrough: Accessing the Web by Using Async and Await (C# and Visual Basic).

    • Create a WPF application that contains a text box and a button. Name the button startButton, and name the text box resultsTextBox.

    • Add a reference for System.Net.Http.

    • In MainWindow.xaml.vb or MainWindow.xaml.cs, add an Imports statement or using directive for System.Net.Http.

To add the code

  1. In the design window, MainWindow.xaml, double-click the button to create the startButton_Click event handler in MainWindow.xaml.vb or MainWindow.xaml.cs. As an alternative, select the button, select the Event handlers for the selected elements icon in the Properties window, and then enter startButton_Click in the Click text box.

  2. Copy the following code, and paste it into the body of startButton_Click in MainWindow.xaml.vb or MainWindow.xaml.cs.

    resultsTextBox.Clear();
    await CreateMultipleTasksAsync();
    resultsTextBox.Text += "\r\n\r\nControl returned to startButton_Click.\r\n";
    

    The code calls an asynchronous method, CreateMultipleTasksAsync, which drives the application.

  3. Add the following support methods to the project:

    • ProcessURLAsync uses an HttpClient method to download the contents of a website as a byte array. The support method, ProcessURLAsync then displays and returns the length of the array.

    • DisplayResults displays the number of bytes in the byte array for each URL. This display shows when each task has finished downloading.

    Copy the following methods, and paste them after the startButton_Click event handler in MainWindow.xaml.vb or MainWindow.xaml.cs.

    async Task<int> ProcessURLAsync(string url, HttpClient client)
    {
        var byteArray = await client.GetByteArrayAsync(url);
        DisplayResults(url, byteArray);
        return byteArray.Length;
    }
    
    
    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);
    }
    
  4. Finally, define method CreateMultipleTasksAsync, which performs the following steps.

    • The method declares an HttpClient object,which you need to access method GetByteArrayAsync in ProcessURLAsync.

    • The method creates and starts three tasks of type Task<TResult>, where TResult is an integer. As each task finishes, DisplayResults displays the task's URL and the length of the downloaded contents. Because the tasks are running asynchronously, the order in which the results appear might differ from the order in which they were declared.

    • The method awaits the completion of each task. Each Await or await operator suspends execution of CreateMultipleTasksAsync until the awaited task is finished. The operator also retrieves the return value from the call to ProcessURLAsync from each completed task.

    • When the tasks have been completed and the integer values have been retrieved, the method sums the lengths of the websites and displays the result.

    Copy the following method, and paste it into your solution.

    private async Task CreateMultipleTasksAsync()
    {
        // Declare an HttpClient object, and increase the buffer size. The 
        // default buffer size is 65,536.
        HttpClient client =
            new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    
        // Create and start the tasks. As each task finishes, DisplayResults  
        // displays its length.
        Task<int> download1 = 
            ProcessURLAsync("http://msdn.microsoft.com", client);
        Task<int> download2 = 
            ProcessURLAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client);
        Task<int> download3 = 
            ProcessURLAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client);
    
        // Await each task. 
        int length1 = await download1;
        int length2 = await download2;
        int length3 = await download3;
    
        int total = length1 + length2 + length3;
    
        // Display the total count for the downloaded websites.
        resultsTextBox.Text +=
            string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
    }
    
  5. Choose the F5 key to run the program, and then choose the Start button.

    Run the program several times to verify that the three tasks don’t always finish in the same order and that the order in which they finish isn't necessarily the order in which they’re created and awaited.

The following code contains the full example.

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 the following using directive, and add a reference for System.Net.Http. 
using System.Net.Http;


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

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();
            await CreateMultipleTasksAsync();
            resultsTextBox.Text += "\r\n\r\nControl returned to startButton_Click.\r\n";
        }


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

            // Create and start the tasks. As each task finishes, DisplayResults  
            // displays its length.
            Task<int> download1 = 
                ProcessURLAsync("http://msdn.microsoft.com", client);
            Task<int> download2 = 
                ProcessURLAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client);
            Task<int> download3 = 
                ProcessURLAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client);

            // Await each task. 
            int length1 = await download1;
            int length2 = await download2;
            int length3 = await download3;

            int total = length1 + length2 + length3;

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


        async Task<int> ProcessURLAsync(string url, HttpClient client)
        {
            var byteArray = await client.GetByteArrayAsync(url);
            DisplayResults(url, byteArray);
            return byteArray.Length;
        }


        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);
        }
    }
}
Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft