处理异步应用重入问题(Visual Basic)

在应用中包含异步代码时,需要考虑并可能予以防止重入,重入是指在异步操作完成之前重新进入该操作。 如果未识别并处理重新进入的可能性,则可能会导致意外结果。

注释

若要运行该示例,必须在计算机上安装 Visual Studio 2012 或更高版本以及 .NET Framework 4.5 或更高版本。

注释

传输层安全性 (TLS) 版本 1.2 现在是应用开发中使用的最低版本。 如果应用面向低于 4.7 的 .NET Framework 版本,请参阅以下文章 ,了解 .NET Framework 的传输层安全性(TLS)最佳做法

识别重新进入

在本主题中的示例中,用户选择“ 开始” 按钮启动一个异步应用,该应用下载了一系列网站,并计算下载的字节总数。 无论用户第一次选择按钮多少次,该示例的同步版本都会以相同的方式做出响应,因为在应用完成运行之前,UI 线程会忽略这些事件。 但是,在异步应用中,UI 线程会继续响应,你可能在异步操作完成之前重新进入异步操作。

以下示例显示用户仅选择 一次“开始” 按钮时的预期输出。 下载的网站列表将显示每个网站的大小(以字节为单位)。 总字节数显示在末尾。

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

TOTAL bytes returned:  890591

但是,如果用户多次选择该按钮,则会重复调用事件处理程序,并且每次重新输入下载过程。 因此,多个异步作同时运行,输出交错结果,总字节数令人困惑。

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

TOTAL bytes returned:  890591

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

TOTAL bytes returned:  890591

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

TOTAL bytes returned:  890591

可以通过滚动到本主题末尾来查看生成此输出的代码。 你可以试验代码,方法是将解决方案下载到本地计算机,然后运行 WebsiteDownload 项目,或使用本主题末尾的代码创建自己的项目。有关详细信息和说明,请参阅 “查看并运行示例应用”。

处理重新进入

可以采用各种方式处理重新进入,具体取决于希望应用执行的操作。 本主题提供以下示例:

  • 禁用“开始”按钮

    在作运行时禁用 “开始” 按钮,以便用户无法中断它。

  • 取消并重启作

    当用户再次选择 “开始” 按钮时,取消仍在运行的任何操作,然后继续执行最近请求的操作。

  • 运行多个操作并将输出排队

    允许所有请求的操作异步运行,但协调输出显示,以便每个操作的结果一起并按顺序显示。

禁用“开始”按钮

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

以下代码显示了这些更改,这些更改用星号标记。 可以在本主题末尾添加对代码的更改,或者从 异步示例:.NET 桌面应用程序中的重入下载已完成的应用程序。 项目名称为 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

由于更改,按钮在下载网站时 AccessTheWebAsync 不会做出响应,因此无法重新输入该过程。

取消并重启操作

可以使“开始”按钮保持活动状态而不是禁用该按钮,但是如果用户再次选择该按钮,则取消已在运行的操作,让最近开始的操作继续运行。

有关取消的详细信息,请参阅 Fine-Tuning 您的异步应用程序(Visual Basic)。

若要设置此方案,请对 审阅和运行示例应用中提供的基本代码进行以下更改。 还可以从 异步示例:在 .NET 桌面应用中重新进入 下载已完成的应用。 此项目的名称为 CancelAndRestart。

  1. 声明一个 CancellationTokenSource 变量, cts该变量位于所有方法的范围内。

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. StartButton_Click 中,确定操作是否已进行。 如果 cts 的值是 Nothing,则没有任何操作是活动的。 如果该值不是Nothing,则会取消正在运行的操作。

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. cts设置为表示当前进程的不同值。

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. StartButton_Click结束时,当前进程已完成,因此请将cts的值设置回Nothing

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    

以下代码显示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

AccessTheWebAsync中,进行以下更改。

  • 添加参数以从 StartButton_Click 接受取消标记。

  • 使用GetAsync 方法下载网站,因为 GetAsync 方法接受 CancellationToken 参数。

  • 在调用 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

如果在运行此应用时多次选择 “开始 ”按钮,它应生成类似于以下输出的结果:

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

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

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

TOTAL bytes returned:  890591

若要消除部分列表,请对 StartButton_Click 中的第一行代码取消注释以在用户每次重新启动操作时清除文本框。

运行多个操作并将输出排入队列

第三个示例是最复杂的,即每次用户选择 “开始 ”按钮时,应用都会启动另一个异步作,并且所有作都运行到完成。 所有请求的操作以异步方式从列表中下载网站,但是操作的输出会按顺序呈现。 也就是说,实际下载活动是交错进行的(如识别重新进入中的输出所示),但是每个组的结果列表会分开呈现。

这些操作共享一个全局Task对象,pendingWork该全局对象在显示过程中充当守护程序。

可以通过将更改粘贴到 生成应用中的代码中来运行此示例,也可以按照 下载应用 中的说明下载示例,然后运行 QueueResults 项目。

如果用户仅选择 一次“开始” 按钮,则以下输出会显示结果。 字母标签“A”指示该结果来源于首次选择“开始”按钮。 这些数字显示下载目标列表中的 URL 的顺序。

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

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

TOTAL bytes returned:  918876

#Group A is complete.

如果用户选择“ 开始 ”按钮三次,应用将生成类似于以下行的输出。 以磅号 (#) 开头的信息行跟踪应用程序的进度。

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

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/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/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

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

#Group A is complete.

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

TOTAL bytes returned:  916097

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

#Group B is complete.

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

TOTAL bytes returned:  920526

#Group C is complete.

组 B 和 C 在组 A 完成之前启动,但每个组的输出单独显示。 首先显示组 A 的所有输出,后跟组 B 的所有输出,然后是组 C 的所有输出。应用始终按顺序显示组,并且对于每个组,始终按照 URL 列表中显示 URL 的顺序显示各个网站的相关信息。

但是,无法预测下载实际发生的顺序。 启动多个组后,它们生成的下载任务都处于活动状态。 你不能假设 A-1 会在 B-1 之前下载,也不能假设 A-1 会在 A-2 之前下载。

全局定义

示例代码包含以下两个全局声明,这些声明在所有方法中可见。

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)

Task 变量 pendingWork负责监督显示过程,并阻止任何组中断另一组的显示作。 字符变量标记 group来自不同组的输出,以验证结果是否按预期顺序显示。

单击事件处理程序

事件处理程序 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

AccessTheWebAsync 方法

此示例拆分 AccessTheWebAsync 为两种方法。 第一种方法 AccessTheWebAsync启动组的所有下载任务,并设置 pendingWork 以控制显示过程。 该方法使用语言集成查询(LINQ 查询)并 ToArray 同时启动所有下载任务。

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

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

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

FinishOneGroupAsync 方法

此方法会循环访问组中的下载任务(等待每个任务、显示下载网站的长度并将该长度添加到总和中)。

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

可以通过将更改粘贴到 生成应用中的代码中来运行此示例,也可以按照 “下载应用 ”中的说明下载示例,然后运行 QueueResults 项目。

兴趣点

输出中以磅号 (#) 开头的信息行阐明了此示例的工作原理。

输出显示以下模式。

  • 在上一个组正在显示其输出时,可以启动新的组,但不会中断上一组的输出显示。

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

  • 以下两行都会一起显示在输出中。 在启动组的操作 StartButton_Click 和为组分配任务 pendingWork 之间,代码从不中断。

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

    组进入 StartButton_Click 之后,操作在操作进入 FinishOneGroupAsync 之前不会完成 await 表达式。 因此,在该代码段期间,任何其他操作都无法获得控制权。

查看并运行示例应用

若要更好地了解示例应用,可以下载它、自行生成或查看本主题末尾的代码,而无需实现应用。

注释

若要将示例作为 Windows Presentation Foundation (WPF) 桌面应用运行,必须在计算机上安装 Visual Studio 2012 或更高版本以及 .NET Framework 4.5 或更高版本。

下载应用

  1. 异步示例:.NET 桌面应用中的可重入性 下载压缩文件。

  2. 解压缩下载的文件,然后启动 Visual Studio。

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

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

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

  6. 选择 Ctrl+F5 键以生成并运行项目。

构建应用程序

以下部分提供代码以 WPF 应用的形式生成示例。

构建 WPF 应用
  1. 启动 Visual Studio。

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

    此时会打开 “新建项目 ”对话框。

  3. “已安装的模板 ”窗格中,展开 Visual Basic,然后展开 Windows

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

  5. 为项目 WebsiteDownloadWPF命名,选择 4.6 或更高版本的 .NET Framework 版本,然后单击“ 确定 ”按钮。

    新项目将显示在 解决方案资源管理器中。

  6. 在 Visual Studio Code 编辑器中,选择 MainWindow.xaml 选项卡。

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

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

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://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的快捷菜单,然后选择“ 查看代码”。

  10. 在MainWindow.xaml.vb中,将代码替换为以下代码。

    ' 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)
            System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12
    
            ' 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/library/hh191443.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/jj155761.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/hh524395.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/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("https://", "")
            ' 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
    
  11. 选择 Ctrl+F5 键以运行程序,然后选择多次 “开始 ”按钮。

  12. 禁用“开始”按钮取消并重启操作运行多个操作并将输出排入队列中进行更改以处理重新进入。

另请参阅