处理异步应用程序中的重入(C# 和 Visual Basic)

在您的应用程序时包括异步代码,应考虑和可以阻止进入,引用重新输入一个异步操作,在完成之前。如果未标识和重新进入的处理可能性,这可能导致意外的结果。

主题内容

说明说明

检查并运行此示例应用程序 的命令显示如何运行代码作为 windows 演示基础 (WPF) 应用程序或作为 windows 存储 app。

若要运行此示例作为 WPF 应用程序,需要 Visual Studio 2012 中,Visual Studio express 2012 中的 windows 桌面或在您的计算机上安装 .NET framework 4.5。

若要运行此示例作为 Windows 应用商店 app,必须在计算机上安装 8 的窗口。此外,因此,如果要从 Visual Studio 运行测试的示例,还必须拥有 Visual Studio 2012 或 Visual Studio express 2012 中的 windows 8 安装。

识别进入

在本主题中的示例中,用户选择 启动 按钮启动下载一系列网站的异步 app 并计算下载的总字节数。该示例的同步版本将响应同一方式无论多少次用户选择按钮,这是因为,在首次后,UI 线程忽略这些事件,直到该 app 完成运行。在异步 app,但是,UI 线程继续执行响应,并且,可能重新输入该异步操作,在完成之前。

如果用户一次只,选择 启动 按钮下面的示例演示预期的输出。下载网站的列表显示了大小,以字节为单位),每个站点。总字节数出现在末尾。

1. msdn.microsoft.com/en-us/library/hh191443.aspx                83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx               205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx                29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx               117152
5. msdn.microsoft.com/en-us/library/hh524395.aspx                68959
6. msdn.microsoft.com/en-us/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/en-us/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

但是,在中,如果用户多次选择按钮,事件处理程序重复调用,并且,下载过程每次重新输入。因此,多个异步操作同时运行,输出交错结果,并且,总字节数造成混淆。

1. msdn.microsoft.com/en-us/library/hh191443.aspx                83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx               205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx                29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx               117152
5. msdn.microsoft.com/en-us/library/hh524395.aspx                68959
1. msdn.microsoft.com/en-us/library/hh191443.aspx                83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx               205273
6. msdn.microsoft.com/en-us/library/ms404677.aspx               197325
3. msdn.microsoft.com/en-us/library/jj155761.aspx                29019
7. msdn.microsoft.com                                            42972
4. msdn.microsoft.com/en-us/library/hh290140.aspx               117152
8. msdn.microsoft.com/en-us/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

5. msdn.microsoft.com/en-us/library/hh524395.aspx                68959
1. msdn.microsoft.com/en-us/library/hh191443.aspx                83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx               205273
6. msdn.microsoft.com/en-us/library/ms404677.aspx               197325
3. msdn.microsoft.com/en-us/library/jj155761.aspx                29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx               117152
7. msdn.microsoft.com                                            42972
5. msdn.microsoft.com/en-us/library/hh524395.aspx                68959
8. msdn.microsoft.com/en-us/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

6. msdn.microsoft.com/en-us/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/en-us/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

可以查看通过将导致此输出到本主题末尾的代码。您可以尝试代码通过下载解决方案到本地计算机然后运行 WebsiteDownload 项目或使用代码本主题末尾创建项目有关更多信息和命令,请参见 检查并运行此示例应用程序

处理进入

根据您可以处理进入以各种方式,您希望 app 的执行。本主题提供以下示例:

  • 禁用开关

    请禁用 启动 按钮,当操作运行时,以便用户不会破坏它。

  • 移除和重新启动操作

    取消仍然运行的所有操作,当用户再次中选择 启动 按钮,然后让最近请求的操作继续。

  • 参与多个操作并对输出

    允许所有请求的操作以异步方式运行,但是,协调输出所示,以便从每个操作的结果显示在一个字符串和顺序。

JJ651641.collapse_all(zh-cn,VS.110).gif禁用开关

您可以阻止 启动 按钮,当操作通过禁用按钮运行时在 StartButton_Click 事件处理程序的顶部。然后可以重新启用按钮从 finally 的内部块操作在完成,以便用户可以再次运行该应用程序。

下面的代码显示这些更改,标记为星号。可以向编码的更改本主题末尾,也可以下载完成的 app 从 Async 示例:在 .NET 阿普斯桌面重新进入Async 示例:重新进入在 windows 中阿普斯。项名称为 DisableStartButton。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' This line is commented out to make the results clearer in the output.
    'ResultsTextBox.Text = ""

    ' ***Disable the Start button until the downloads are complete. 
    StartButton.IsEnabled = False

    Try
        Await AccessTheWebAsync()

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    ' ***Enable the Start button in case you want to run the program again. 
    Finally
        StartButton.IsEnabled = True

    End Try
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // This line is commented out to make the results clearer in the output.
    //ResultsTextBox.Text = "";

    // ***Disable the Start button until the downloads are complete. 
    StartButton.IsEnabled = false; 

    try
    {
        await AccessTheWebAsync();
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.";
    }
    // ***Enable the Start button in case you want to run the program again. 
    finally
    {
        StartButton.IsEnabled = true;
    }
}

由于进行了更改,按钮不响应,当 AccessTheWebAsync 下载网站时,因此,过程不会重新输入。

JJ651641.collapse_all(zh-cn,VS.110).gif移除和重新启动操作

而不是禁用 启动 按钮,可以保留按钮激活,但,因此,如果用户再次选择该按钮,取消已运行并允许最近开始操作继续操作。

有关取消操作的更多信息,请参见 优化您的异步应用程序

若要设置此方案,请对 检查并运行此示例应用程序提供的基本代码进行以下更改。还可以下载完成的 app 从 Async 示例:在 .NET 阿普斯桌面重新进入Async 示例:重新进入在 windows 中阿普斯。此项目的名称为 CancelAndRestart。

  1. 声明 CancellationTokenSource 变量,cts,在任何方法的范围。

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
    public partial class MainWindow : Window   // Or class MainPage
    {
        // *** Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;
    
  2. 在 StartButton_Click,请确定操作是否正在进行。如果 cts 的值为 null (在 Visual Basic 中Nothing ),操作尚未处于活动状态。如果值不为空,已运行的操作将取消。

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
    // *** If a download process is already underway, cancel it.
    if (cts != null)
    {
        cts.Cancel();
    }
    
  3. 设置 cts 到表示当前进程的值不同。

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
    // *** Now set cts to a new value that you can use to cancel the current process
    // if the button is chosen again.
    CancellationTokenSource newCTS = new CancellationTokenSource();
    cts = newCTS;
    
  4. 在 StartButton_Click结束时,当前进程完成的,因此将 cts 的值更改为 NULL。

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    
    // *** When the process is complete, signal that another process can begin.
    if (cts == newCTS)
        cts = null;
    

下面的代码演示在 StartButton_Click中的任何更改。添加标记为星号。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

    ' This line is commented out to make the results clearer. 
    'ResultsTextBox.Text = ""


    ' *** If a download process is underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS

    Try
        ' *** Send a token to carry the message if the operation is canceled.
        Await AccessTheWebAsync(cts.Token)
        

    Catch ex As OperationCanceledException
        ResultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    End Try

    ' *** When the process is complete, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // This line is commented out to make the results clearer in the output.
    //ResultsTextBox.Clear();

    // *** If a download process is already underway, cancel it.
    if (cts != null)
    {
        cts.Cancel();
    }

    // *** Now set cts to cancel the current process if the button is chosen again.
    CancellationTokenSource newCTS = new CancellationTokenSource();
    cts = newCTS;

    try
    {
        // ***Send cts.Token to carry the message if there is a cancellation request.
        await AccessTheWebAsync(cts.Token);
        
    }
    // *** Catch cancellations separately.
    catch (OperationCanceledException)
    {
        ResultsTextBox.Text += "\r\nDownloads canceled.\r\n";
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.\r\n";
    }
    // *** When the process is complete, signal that another process can proceed.
    if (cts == newCTS)
        cts = null;
}

在 AccessTheWebAsync,请进行以下更改。

  • 将一个参数接受来自 StartButton_Click的取消标记。

  • 因为 GetAsync 接受 CancellationToken 变量,请使用 GetAsync 方法下载网站。

  • 在调用 DisplayResults 之前显示每个下载网站的结果,检查 ct 验证当前操作未取消。

下面的代码显示这些更改,标记为星号。

' *** Provide a parameter for the CancellationToken from StartButton_Click.
Private Async Function AccessTheWebAsync(ct As CancellationToken) As Task

    ' Declare an HttpClient object.
    Dim client = New HttpClient()

    ' Make a list of web addresses.
    Dim urlList As List(Of String) = SetUpURLList()

    Dim total = 0
    Dim position = 0

    For Each url In urlList
        ' *** Use the HttpClient.GetAsync method because it accepts a 
        ' cancellation token.
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' *** Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' *** Check for cancellations before displaying information about the 
        ' latest site. 
        ct.ThrowIfCancellationRequested()

        position += 1
        DisplayResults(url, urlContents, position)

        ' Update the total.
        total += urlContents.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function
// *** Provide a parameter for the CancellationToken from StartButton_Click.
async Task AccessTheWebAsync(CancellationToken ct)
{
    // Declare an HttpClient object.
    HttpClient client = new HttpClient();

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

    var total = 0;
    var position = 0;

    foreach (var url in urlList)
    {
        // *** Use the HttpClient.GetAsync method because it accepts a 
        // cancellation token.
        HttpResponseMessage response = await client.GetAsync(url, ct);

        // *** Retrieve the website contents from the HttpResponseMessage.
        byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

        // *** Check for cancellations before displaying information about the 
        // latest site. 
        ct.ThrowIfCancellationRequested();

        DisplayResults(url, urlContents, ++position);

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

如果选择 启动 几次按钮,则此应用程序运行时,它应生成类似于下面的输出的结果。

1. msdn.microsoft.com/en-us/library/hh191443.aspx                83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx               205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx                29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx               122505
5. msdn.microsoft.com/en-us/library/hh524395.aspx                68959
6. msdn.microsoft.com/en-us/library/ms404677.aspx               197325
Download canceled.

1. msdn.microsoft.com/en-us/library/hh191443.aspx                83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx               205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx                29019
Download canceled.

1. msdn.microsoft.com/en-us/library/hh191443.aspx                83732
2. msdn.microsoft.com/en-us/library/aa578028.aspx               205273
3. msdn.microsoft.com/en-us/library/jj155761.aspx                29019
4. msdn.microsoft.com/en-us/library/hh290140.aspx               117152
5. msdn.microsoft.com/en-us/library/hh524395.aspx                68959
6. msdn.microsoft.com/en-us/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/en-us/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

用户每次重新启动操作,若要消除节中,取消注释第一个代码行中 StartButton_Click 的清除文本框。

JJ651641.collapse_all(zh-cn,VS.110).gif参与多个操作并对输出

第三个示例是最复杂的因为该应用程序每次启动另一个异步操作用户选择 启动 按钮和所有操作参与已完成"。所有请求的操作可从列表的网站异步,但是,按顺序存在于操作的输出。即实际下载的事件交错,因为,在 识别进入 的输出所示,但是,分别存在结果列表的每个组。

操作共享全局 Task,pendingWork,可用作一网关控制用作该显示进程。

可以通过下列方式更改遇到此代码示例在 生成 App上,也可以按照 下载 App 的命令下载示例然后运行 QueueResults 项目。

如果用户一次只,选择 启动 按钮下面的输出显示了结果。字母),则,指示该结果是从 启动 按钮首次被选中。数字显示 URL 的顺序在列表中下载目标。

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/en-us/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/en-us/library/aa578028.aspx               209858
A-3. msdn.microsoft.com/en-us/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/en-us/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/en-us/library/hh524395.aspx                71260
A-6. msdn.microsoft.com/en-us/library/ms404677.aspx               199186
A-7. msdn.microsoft.com                                            53266
A-8. msdn.microsoft.com/en-us/library/ff730837.aspx               148020

TOTAL bytes returned:  918876


#Group A is complete.

如果用户选择 启动 按钮三次,app 导致类似于下面的行的输出。以井号的信息行 (#) 应用程序的跟踪进度。

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/en-us/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/en-us/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/en-us/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/en-us/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/en-us/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/en-us/library/ms404677.aspx               199185

#Starting group B.
#Task assigned for group B.

A-7. msdn.microsoft.com                                            53266

#Starting group C.
#Task assigned for group C.

A-8. msdn.microsoft.com/en-us/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

B-1. msdn.microsoft.com/en-us/library/hh191443.aspx                87389
B-2. msdn.microsoft.com/en-us/library/aa578028.aspx               207089
B-3. msdn.microsoft.com/en-us/library/jj155761.aspx                30870
B-4. msdn.microsoft.com/en-us/library/hh290140.aspx               119027
B-5. msdn.microsoft.com/en-us/library/hh524395.aspx                71260
B-6. msdn.microsoft.com/en-us/library/ms404677.aspx               199186

#Group A is complete.

B-7. msdn.microsoft.com                                            53266
B-8. msdn.microsoft.com/en-us/library/ff730837.aspx               148010

TOTAL bytes returned:  916097

C-1. msdn.microsoft.com/en-us/library/hh191443.aspx                87389
C-2. msdn.microsoft.com/en-us/library/aa578028.aspx               207089

#Group B is complete.

C-3. msdn.microsoft.com/en-us/library/jj155761.aspx                30870
C-4. msdn.microsoft.com/en-us/library/hh290140.aspx               119027
C-5. msdn.microsoft.com/en-us/library/hh524395.aspx                72765
C-6. msdn.microsoft.com/en-us/library/ms404677.aspx               199186
C-7. msdn.microsoft.com                                            56190
C-8. msdn.microsoft.com/en-us/library/ff730837.aspx               148010

TOTAL bytes returned:  920526

#Group C is complete.

组 B 和 C 开头,在组中完成之前,但是,每个组的输出单独出现。组的所有输出首先出现,后跟组的 B 所有输出,然后组的 C. 任何输出。该应用程序始终显示组按顺序,因此,每个组的,始终显示单个网站的信息按顺序 URL 显示在 URL 列表。

但是,您无法预测实际上下载中出现的顺序。在多个组启动后,它们生成的下载任务有效中。您不能假定,A-1 在 B-1 之前将下载,因此,您不能假定,A-1 在 A-2 之前将下载。

JJ651641.collapse_all(zh-cn,VS.110).gif全局定义

代码示例包含所有方法可见的以下两个全局声明。

Class MainWindow    ' Class MainPage in Windows Store app.

    ' ***Declare the following variables where all methods can access them. 
    Private pendingWork As Task = Nothing
    Private group As Char = ChrW(AscW("A") - 1)
public partial class MainWindow : Window  // Class MainPage in Windows Store app.
{
    // ***Declare the following variables where all methods can access them. 
    private Task pendingWork = null;   
    private char group = (char)('A' - 1);

Task 变量,pendingWork,管理员将显示进程并防止任何组使另一个组的显示操作。字符变量,group,标记从不同的组的输出验证结果按期望的顺序显示。

JJ651641.collapse_all(zh-cn,VS.110).gifclick 事件处理程序

事件处理程序,StartButton_Click,增量组字母,每次用户选择 启动 按钮。然后处理程序调用 AccessTheWebAsync 参与这次下载的操作。

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' ***Verify that each group's results are displayed together, and that
    ' the groups display in order, by marking each group with a letter.
    group = ChrW(AscW(group) + 1)
    ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Starting group {0}.", group)

    Try
        ' *** Pass the group value to AccessTheWebAsync.
        Dim finishedGroup As Char = Await AccessTheWebAsync(group)

        ' The following line verifies a successful return from the download and 
        ' display procedures. 
        ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Group {0} is complete." & vbCrLf, finishedGroup)

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."

    End Try
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // ***Verify that each group's results are displayed together, and that
    // the groups display in order, by marking each group with a letter.
    group = (char)(group + 1);
    ResultsTextBox.Text += string.Format("\r\n\r\n#Starting group {0}.", group);

    try
    {
        // *** Pass the group value to AccessTheWebAsync.
        char finishedGroup = await AccessTheWebAsync(group);

        // The following line verifies a successful return from the download and
        // display procedures. 
        ResultsTextBox.Text += string.Format("\r\n\r\n#Group {0} is complete.\r\n", finishedGroup);
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.";
    }
}

JJ651641.collapse_all(zh-cn,VS.110).gifAccessTheWebAsync 方法

此示例 AccessTheWebAsync 拆分为两个方法。第一种方法,AccessTheWebAsync,启动组的所有下载任务并将 pendingWork 控件显示进程。方法使用一次语言集成查询 (LINQ 查询) 和 ToArray<TSource> 同时启动下载所有任务。

AccessTheWebAsync 然后调用 FinishOneGroupAsync 等待每个的完成下载并显示其长度。

FinishOneGroupAsync 返回分配给 AccessTheWebAsync的 pendingWork 的任务。值由另一个操作防止中断,在任务完成之前。

Private Async Function AccessTheWebAsync(grp As Char) As Task(Of Char)

    Dim client = New HttpClient()

    ' Make a list of the web addresses to download.
    Dim urlList As List(Of String) = SetUpURLList()

    ' ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Dim getContentTasks As Task(Of Byte())() =
        urlList.Select(Function(addr) client.GetByteArrayAsync(addr)).ToArray()

    ' ***Call the method that awaits the downloads and displays the results.
    ' Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp)

    ResultsTextBox.Text &=
        String.Format(vbCrLf & "#Task assigned for group {0}. Download tasks are active." & vbCrLf, grp)

    ' ***This task is complete when a group has finished downloading and displaying.
    Await pendingWork

    ' You can do other work here or just return.
    Return grp
End Function
private async Task<char> AccessTheWebAsync(char grp)
{
    HttpClient client = new HttpClient();

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

    // ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Task<byte[]>[] getContentTasks = urlList.Select(url => client.GetByteArrayAsync(url)).ToArray();

    // ***Call the method that awaits the downloads and displays the results.
    // Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp);

    ResultsTextBox.Text += string.Format("\r\n#Task assigned for group {0}. Download tasks are active.\r\n", grp);

    // ***This task is complete when a group has finished downloading and displaying.
    await pendingWork;

    // You can do other work here or just return.
    return grp;
}

JJ651641.collapse_all(zh-cn,VS.110).gifFinishOneGroupAsync 方法

此方法通过下载任务在组中循环,等待每个元素,显示该下载网站的长度并将该长度为总计。

在 FinishOneGroupAsync 的第一个语句使用 pendingWork,以确保输入方法不会影响在显示已处理或已挂起的操作。如果此类操作正在进行中,输入的操作必须等待其关闭。

Private Async Function FinishOneGroupAsync(urls As List(Of String), contentTasks As Task(Of Byte())(), grp As Char) As Task

    ' Wait for the previous group to finish displaying results.
    If pendingWork IsNot Nothing Then
        Await pendingWork
    End If

    Dim total = 0

    ' contentTasks is the array of Tasks that was created in AccessTheWebAsync.
    For i As Integer = 0 To contentTasks.Length - 1
        ' Await the download of a particular URL, and then display the URL and
        ' its length.
        Dim content As Byte() = Await contentTasks(i)
        DisplayResults(urls(i), content, i, grp)
        total += content.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function
private async Task FinishOneGroupAsync(List<string> urls, Task<byte[]>[] contentTasks, char grp)
{
    // ***Wait for the previous group to finish displaying results.
    if (pendingWork != null) await pendingWork;

    int total = 0;

    // contentTasks is the array of Tasks that was created in AccessTheWebAsync.
    for (int i = 0; i < contentTasks.Length; i++)
    {
        // Await the download of a particular URL, and then display the URL and
        // its length.
        byte[] content = await contentTasks[i];
        DisplayResults(urls[i], content, i, grp);
        total += content.Length;
    }

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

可以通过下列方式更改遇到此代码示例在 生成 App上,也可以按照 下载 App 的命令下载示例,然后运行 QueueResults 项目。

JJ651641.collapse_all(zh-cn,VS.110).gif指向相关

从 (#) 符号开始的信息行 (#) 在输出阐明此示例演示如何工作。

输出显示以下模式。

  • 组可以启动,在前一个组显示其输出时,但是,没有破坏以前的组的输出的显示。

    #Starting group A.
    #Task assigned for group A. Download tasks are active.
    
    A-1. msdn.microsoft.com/en-us/library/hh191443.aspx                87389
    A-2. msdn.microsoft.com/en-us/library/aa578028.aspx               207089
    A-3. msdn.microsoft.com/en-us/library/jj155761.aspx                30870
    A-4. msdn.microsoft.com/en-us/library/hh290140.aspx               119037
    A-5. msdn.microsoft.com/en-us/library/hh524395.aspx                71260
    
    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    
    A-6. msdn.microsoft.com/en-us/library/ms404677.aspx               199186
    A-7. msdn.microsoft.com                                            53078
    A-8. msdn.microsoft.com/en-us/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915919
    
    B-1. msdn.microsoft.com/en-us/library/hh191443.aspx                87388
    B-2. msdn.microsoft.com/en-us/library/aa578028.aspx               207089
    B-3. msdn.microsoft.com/en-us/library/jj155761.aspx                30870
    
    #Group A is complete.
    
    B-4. msdn.microsoft.com/en-us/library/hh290140.aspx               119027
    B-5. msdn.microsoft.com/en-us/library/hh524395.aspx                71260
    B-6. msdn.microsoft.com/en-us/library/ms404677.aspx               199186
    B-7. msdn.microsoft.com                                            53078
    B-8. msdn.microsoft.com/en-us/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915908
    
  • pendingWork 任务仅在 FinishOneGroupAsync 的开头为 null (在 Visual Basic 中Nothing ) 组的,首次启动。在到达 FinishOneGroupAsync时,组中未完成一个等待表达式。因此,控件未返回到 AccessTheWebAsync,并且,对于 pendingWork 的第一个分配尚未发生。

  • 以下两行时始终出现在输出。代码始终不会中断开始在 StartButton_Click 组的操作和指派任务之间组的。pendingWork。

    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    

    在组输入 StartButton_Click之后,操作无法完成一个等待表达式,直到操作输入 FinishOneGroupAsync。因此,在代码中该时间段内,其他操作无法访问控件。

检查并运行此示例应用程序

更好地了解该示例应用程序,则可以进行下载,生成该 hello 或检查代码本主题结尾,而无需实现该应用程序。

说明说明

若要运行此示例作为 windows 演示基础 (WPF) 桌面应用程序,需要 Visual Studio 2012 中,Visual Studio express 2012 中的 windows 桌面或在您的计算机上安装 .NET framework 4.5。

若要运行此示例作为 Windows 应用商店 app,必须在计算机上安装 8 的窗口。此外,因此,如果要从 Visual Studio 运行测试的示例,还必须拥有 Visual Studio 2012 或 Visual Studio express 2012 中的 windows 8 安装。Visual Studio 2010 无法加载为 .NET framework 4.5 为目标的项目。

JJ651641.collapse_all(zh-cn,VS.110).gif下载 App

  1. 下载压缩文件从 Async 示例:在 .NET 阿普斯桌面重新进入Async 示例:重新进入在 windows 中阿普斯

  2. 对要下载的文件,然后启动 Visual Studio。

  3. 在菜单栏上,依次选择**“文件”“打开”“项目/解决方案”**。

  4. 导航到保存进行解压缩的代码示例的文件夹,然后打开解决方案文件 (.sln)。

  5. 解决方案资源管理器,打开要运行的项目的快捷菜单,然后选择 设置为 StartUpProject

  6. 选择 CTRL+F5 键生成和运行项目。

JJ651641.collapse_all(zh-cn,VS.110).gif生成 App

以下各节提供了代码生成此示例作为 WPF 应用程序或作为 Windows 应用商店 app。

生成 WPF 应用程序

  1. 启动 Visual Studio 2012。

  2. 在菜单栏上,选择**“文件”“新建**、“项目”

    将打开**“新建项目”**对话框。

  3. 已安装的模板 窗格中,展开 Visual BasicVisual C#,然后展开 窗口

  4. 在"项目类型列表中,选择 WPF 应用程序

  5. 将项目命名为 WebsiteDownloadWPF,然后选择 确定 按钮。

    新项目出现在**“解决方案资源管理器”**中。

  6. 在 Visual Studio 代码编辑器"中,选择 MainWindow.xaml 选项。

    如果看不到选项卡,打开 MainWindow.xaml 的快捷菜单在 解决方案资源管理器,然后选择 查看代码

  7. 在 MainWindow.xaml XAML 视图中,用以下代码替换代码。

    <Window x:Class="MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    
    <Window x:Class="WebsiteDownloadWPF.MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    

    包含文本框和一个按钮的简单窗口显示 MainWindow.xaml 设计 视图。

  8. 添加 System.Net.Http的引用。

  9. 解决方案资源管理器,请打开 MainWindow.xaml.vb 或 MainWindow.xaml.cs 的快捷菜单,然后选择 查看代码

  10. 在 MainWindow.xaml.vb 或 MainWindow.xaml.cs 中,用以下代码替换代码。

    ' Add the following Imports statements, and add a reference for System.Net.Http.
    Imports System.Net.Http
    Imports System.Threading
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ' This line is commented out to make the results clearer in the output.
            'ResultsTextBox.Text = ""
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    
            End Try
        End Sub
    
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object.
            Dim client = New HttpClient()
    
            ' Make a list of web addresses.
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task
                ' produces a byte array.
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function
    
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
            Return urls
        End Function
    
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' 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.
    
            ' Strip off the "http:'".
            Dim displayURL = url.Replace("http://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        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 directives, and add a reference for System.Net.Http.
    using System.Net.Http;
    using System.Threading;
    
    namespace WebsiteDownloadWPF
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                // This line is commented out to make the results clearer in the output.
                //ResultsTextBox.Text = "";
    
                try
                {
                    await AccessTheWebAsync();
                }
                catch (Exception)
                {
                    ResultsTextBox.Text += "\r\nDownloads failed.";
                }
            }
    
    
            private async Task AccessTheWebAsync()
            {
                // Declare an HttpClient object.
                HttpClient client = new HttpClient();
    
                // Make a list of web addresses.
                List<string> urlList = SetUpURLList();
    
                var total = 0;
                var position = 0;
    
                foreach (var url in urlList)
                {
                    // GetByteArrayAsync returns a task. At completion, the task
                    // produces a byte array.
                    byte[] urlContents = await client.GetByteArrayAsync(url);
    
                    DisplayResults(url, urlContents, ++position);
    
                    // 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> 
                { 
                    "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                    "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                    "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                    "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                    "https://msdn.microsoft.com",
                    "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
                };
                return urls;
            }
    
    
            private void DisplayResults(string url, byte[] content, int pos)
            {
                // 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.
    
                // Strip off the "http://".
                var displayURL = url.Replace("http://", "");
                // Display position in the URL list, the URL, and the number of bytes.
                ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length);
            }
        }
    }
    
  11. 选择 CTRL+F5 键运行程序,然后选择 启动 几次按钮。

  12. 使从 禁用开关移除和重新启动操作参与多个操作并对输出 的更改处理进入。

若要生成窗口存储 app

  1. 启动 Visual Studio 2012。

  2. 在菜单栏上,选择**“文件”“新建**、“项目”

    将打开**“新建项目”**对话框。

  3. 已安装模板 类别,展开 Visual BasicVisual C#,然后展开 Windows 应用商店

  4. 在"项目类型列表中,选择 空白应用程序 (XAML)

  5. 将项目命名为 WebsiteDownloadWin,然后选择 确定 按钮。

    新项目出现在**“解决方案资源管理器”**中。

  6. 解决方案资源管理器,打开 MainPage.xaml 的快捷菜单,然后选择 打开

  7. XAML 窗口 MainPage.xaml 中,用下面的代码替换代码。

    <Page
        x:Class="WebsiteDownloadWin.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWin"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" FontSize="12">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="325,77,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="145" Background="#FFA89B9B" FontSize="36" Width="711"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="325,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="711" FontFamily="Lucida Console" />
        </Grid>
    </Page>
    

    包含文本框和一个 启动 按钮的简单窗口显示 设计 窗口 MainPage.xaml。

  8. 解决方案资源管理器,打开 MainPage.xaml.vb 或 MainPage.xaml.cs 的快捷菜单,然后选择 查看代码

  9. 用下面的代码替换该 MainPage.xaml.vb 或 MainPage.xaml.cs 的代码。

    ' Add the following Imports statements.
    Imports System.Threading.Tasks
    Imports System.Threading
    Imports System.Net.Http
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        End Sub
    
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ' This line is commented out to make the results clearer in the output.
            'ResultsTextBox.Text = ""
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    
            End Try
        End Sub
    
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object.
            Dim client = New HttpClient()
    
            ' Make a list of web addresses.
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task
                ' produces a byte array.
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function
    
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
            Return urls
        End Function
    
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' 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.
    
            ' Strip off the "http:'".
            Dim displayURL = url.Replace("http://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // Add the following using directives. 
    using System.Threading.Tasks;
    using System.Threading;
    using System.Net.Http;
    
    
    namespace WebsiteDownloadWin
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                // This line is commented out to make the results clearer in the output.
                //ResultsTextBox.Text = "";
    
                try
                {
                    await AccessTheWebAsync();
                }
                catch (Exception)
                {
                    ResultsTextBox.Text += "\r\nDownloads failed.";
                }
            }
    
    
            private async Task AccessTheWebAsync()
            {
                // Declare an HttpClient object.
                HttpClient client = new HttpClient();
    
                // Make a list of web addresses.
                List<string> urlList = SetUpURLList();
    
                var total = 0;
                var position = 0;
    
                foreach (var url in urlList)
                {
                    // GetByteArrayAsync returns a task. At completion, the task
                    // produces a byte array.
                    byte[] urlContents = await client.GetByteArrayAsync(url);
    
                    DisplayResults(url, urlContents, ++position);
    
                    // 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> 
                { 
                    "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                    "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                    "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                    "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                    "https://msdn.microsoft.com",
                    "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
                };
                return urls;
            }
    
    
            private void DisplayResults(string url, byte[] content, int pos)
            {
                // 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.
    
                // Strip off the "http://".
                var displayURL = url.Replace("http://", "");
                // Display position in the URL list, the URL, and the number of bytes.
                ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length);
            }
        }
    }
    
  10. 选择 CTRL+F5 键运行程序,然后选择 启动 几次按钮。

  11. 使从 禁用开关移除和重新启动操作参与多个操作并对输出 的更改处理进入。

请参见

任务

演练:使用 Async 和 Await 访问 Web(C# 和 Visual Basic)

概念

使用 Async 和 Await 的异步编程(C# 和 Visual Basic)

其他资源

异步编程 (windows 存储 apps)

快速入门:调用 c# 或 Visual Basic 中的异步 API