如何:使用 Async 和 Await 并行发起多个 Web 请求(C# 和 Visual Basic)
在异步方法中,任务创建时即启动。 等待 (Visual Basic) 或等待 (c#) 运算符应用于在任务完成前进程无法继续的方法中该点的任务。 如下例所示,通常任务一经创建即进入等待。
Dim result = Await someWebAccessMethodAsync(url)
var result = await someWebAccessMethodAsync(url);
但是,如果您的程序有其他工作要完成,这些工作不依赖于任务完成,则可以将创建任务与等待任务分开。
' The following line creates and starts the task.
Dim myTask = someWebAccessMethodAsync(url)
' While the task is running, you can do other work that does not depend
' on the results of the task.
' . . . . .
' The application of Await suspends the rest of this method until the task is
' complete.
Dim result = Await myTask
// 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;
在启动任务和等待任务之间,可以开始其他任务。 其他任务并行隐式运行,但不会创建其他线程。
下面的过程启动三个异步 web 下载,然后按调用它们的顺序等待它们。 请注意,当您运行程序时,任务并不会总是以创建和等待的顺序完成。 它们在创建后立即开始运行,因此一个或多个任务在方法到达 await 表达式之前可能已经完成。
备注
要完成此项目,你必须在计算机上安装 Visual Studio 2012、Visual Studio 2013、Visual Studio Express 2012 for Windows Desktop、Visual Studio Express 2013 for Windows 或 .NET Framework 4.5 或 4.5.1。
有关同时启动多个任务的其他示例,请参见 如何:使用 Task.WhenAll 扩展异步演练(C# 和 Visual Basic)。
可从 Developer Code Samples(开发人员代码示例)下载此示例的代码。
设置项目
要设置 WPF 应用程序,请完成以下步骤。 您可以在演练:使用 Async 和 Await 访问 Web(C# 和 Visual Basic)中找到这些步骤的详细说明。
创建包含文本框和按钮的 WPF 应用程序。 命名按钮 startButton,并命名文本框 resultsTextBox。
添加对 System.Net.Http 的引用。
在 MainWindow.xaml.vb 或 MainWindow.xaml.cs 中,针对 System.Net.Http 添加 Imports 语句或 using 指令。
添加代码
在 MainWindow.xaml 的设计窗口中,双击该按钮以在 MainWindow.xaml.vb 或 MainWindow.xaml.cs 中创建 startButton_Click 事件处理程序。 或者,选择此按钮,选择“属性”窗口中的“所选元素的事件处理程序”图标,然后在“单击”文本框中输入 startButton_Click。
复制下列代码,然后将其粘贴到 MainWindow.xaml.vb 或 MainWindow.xaml.cs.中 startButton_Click 的主体。
resultsTextBox.Clear() Await CreateMultipleTasksAsync() resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
resultsTextBox.Clear(); await CreateMultipleTasksAsync(); resultsTextBox.Text += "\r\n\r\nControl returned to startButton_Click.\r\n";
代码调用驱动应用程序的异步方法 CreateMultipleTasksAsync。
将以下支持方法添加到项目中:
ProcessURLAsync 使用 HttpClient 方法以字节数组的形式从网站下载内容。 支持方法,ProcessURLAsync 然后显示并返回数组的长度。
DisplayResults 显示每个 URL 的字节数组中的字节数。 此显示演示每个任务何时完成下载。
复制以下方法,然后将其粘贴在 MainWindow.xaml.vb 或 MainWindow.xaml.cs 中的 startButton_Click 事件处理程序后。
Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer) Dim byteArray = Await client.GetByteArrayAsync(url) DisplayResults(url, byteArray) Return byteArray.Length End Function Private Sub DisplayResults(url As String, content As Byte()) ' 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. Dim bytes = content.Length ' Strip off the "http://". Dim displayURL = url.Replace("http://", "") resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes) End Sub
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); }
最后,定义执行以下步骤的方法 CreateMultipleTasksAsync。
该方法声明 HttpClient 对象,你需要在 ProcessURLAsync 中访问方法 GetByteArrayAsync。
该方法创建并启动类型 Task 的三个任务,其中 TResult 是整数。 每个任务完成时,DisplayResults 显示任务的 URL 和下载内容的长度。 由于任务以异步方式运行,因此结果显示的顺序可能与其声明的顺序不同。
该方法等待每项任务的完成。 每个 Await 或 await 运算符挂起 CreateMultipleTasksAsync 的执行,直到等待的任务完成。 运算符还从每个完成的任务中对 ProcessURLAsync 的调用中检索返回值。
任务已完成且整数值已检索出后,该方法会计算网站的总长并显示结果。
复制以下方法并将其粘贴到解决方案中。
Private Async Function CreateMultipleTasksAsync() As Task ' Declare an HttpClient object, and increase the buffer size. The ' default buffer size is 65,536. Dim client As HttpClient = New HttpClient() With {.MaxResponseContentBufferSize = 1000000} ' Create and start the tasks. As each task finishes, DisplayResults ' displays its length. Dim download1 As Task(Of Integer) = ProcessURLAsync("https://msdn.microsoft.com", client) Dim download2 As Task(Of Integer) = ProcessURLAsync("https://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client) Dim download3 As Task(Of Integer) = ProcessURLAsync("https://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client) ' Await each task. Dim length1 As Integer = Await download1 Dim length2 As Integer = Await download2 Dim length3 As Integer = Await download3 Dim total As Integer = length1 + length2 + length3 ' Display the total count for all of the websites. resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "Total bytes returned: {0}" & vbCrLf, total) End Function
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("https://msdn.microsoft.com", client); Task<int> download2 = ProcessURLAsync("https://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client); Task<int> download3 = ProcessURLAsync("https://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); }
选择 F5 键运行程序,然后选择“开始”按钮。
多次运行程序以验证三个任务并非总是以相同顺序完成,且完成顺序并非即其创建和等待的顺序。
示例
下面的代码包括完整的示例。
' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Class MainWindow
Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click
resultsTextBox.Clear()
Await CreateMultipleTasksAsync()
resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
End Sub
Private Async Function CreateMultipleTasksAsync() As Task
' Declare an HttpClient object, and increase the buffer size. The
' default buffer size is 65,536.
Dim client As HttpClient =
New HttpClient() With {.MaxResponseContentBufferSize = 1000000}
' Create and start the tasks. As each task finishes, DisplayResults
' displays its length.
Dim download1 As Task(Of Integer) =
ProcessURLAsync("https://msdn.microsoft.com", client)
Dim download2 As Task(Of Integer) =
ProcessURLAsync("https://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client)
Dim download3 As Task(Of Integer) =
ProcessURLAsync("https://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client)
' Await each task.
Dim length1 As Integer = Await download1
Dim length2 As Integer = Await download2
Dim length3 As Integer = Await download3
Dim total As Integer = length1 + length2 + length3
' Display the total count for all of the websites.
resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
"Total bytes returned: {0}" & vbCrLf, total)
End Function
Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)
Dim byteArray = Await client.GetByteArrayAsync(url)
DisplayResults(url, byteArray)
Return byteArray.Length
End Function
Private Sub DisplayResults(url As String, content As Byte())
' 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.
Dim bytes = content.Length
' Strip off the "http://".
Dim displayURL = url.Replace("http://", "")
resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
End Sub
End Class
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("https://msdn.microsoft.com", client);
Task<int> download2 =
ProcessURLAsync("https://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client);
Task<int> download3 =
ProcessURLAsync("https://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);
}
}
}
请参见
任务
演练:使用 Async 和 Await 访问 Web(C# 和 Visual Basic)
如何:使用 Task.WhenAll 扩展异步演练(C# 和 Visual Basic)