通过


启动多个异步任务,并在完成时对其进行处理(Visual Basic)

通过使用 Task.WhenAny,可以同时启动多个任务,并在完成时逐个处理它们,而不是按照启动顺序处理它们。

以下示例使用查询创建任务集合。 每个任务都会下载指定网站的内容。 在对 while 循环的每次迭代中,对 WhenAny 的等待调用返回任务集合中首先完成下载的任务。 此任务从集合中删除并进行处理。 循环重复,直到集合不包含更多任务。

注释

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

下载示例

可以从异步示例下载完整的 Windows Presentation Foundation (WPF) 项目 :微调应用程序 ,然后执行以下步骤。

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

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

  3. “打开项目 ”对话框中,打开保存解压缩的示例代码的文件夹,然后打开 AsyncFineTuningVB 的解决方案(.sln)文件。

  4. 解决方案资源管理器中,打开 ProcessTasksAsTheyFinish 项目的快捷菜单,然后选择 “设置为启动项目”。

  5. 选择要运行项目的 F5 键。

    选择 Ctrl+F5 键以运行项目而不对其进行调试。

  6. 多次运行项目,验证下载的长度并不总是按相同的顺序显示。

如果不想下载项目,可以在本主题末尾查看MainWindow.xaml.vb文件。

生成示例

该示例扩展了“在完成一个任务后取消剩余异步任务(Visual Basic)” 中开发的代码,并使用相同的 UI。

若要自行生成示例,请逐步按照“下载示例”部分中的说明进行作,但选择 CancelAfterOneTask 作为 启动项目。 将本主题中的更改添加到该项目中的 AccessTheWebAsync 方法。 这些更改用星号标记。

CancelAfterOneTask 项目已包含一个查询,该查询在执行时会创建任务集合。 以下代码中,每次调用ProcessURLAsync都会返回一个Task<TResult>,其中TResult是整数。

Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
    From url In urlList Select ProcessURLAsync(url, client, ct)

在项目的MainWindow.xaml.vb文件中,对 AccessTheWebAsync 方法进行以下更改。

  • 通过应用Enumerable.ToList而不是ToArray来执行查询。

    Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
    
  • 添加一个 while 循环,该循环针对集合中的每个任务执行以下步骤。

    1. 等待调用 WhenAny,以标识集合中首个完成下载的任务。

      Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
      
    2. 从集合中删除该任务。

      downloadTasks.Remove(finishedTask)
      
    3. 等待 finishedTask,由对 ProcessURLAsync 的调用返回。 变量finishedTask是一个Task<TResult>,其中TReturn是整数。 任务已经完成,但你等待它检索下载的网站长度,如以下示例所示。

      Dim length = Await finishedTask
      resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
      

应多次运行项目,以验证下载的长度并不总是以相同的顺序显示。

谨慎

如示例中所述,可以在循环中使用 WhenAny ,以解决涉及少量任务的问题。 但是,如果你有大量要处理的任务,则其他方法更高效。 有关详细信息和示例,请参阅 Processing Tasks as they complete(在任务完成时处理它们)。

完整的示例

以下代码是示例MainWindow.xaml.vb文件的完整文本。 星号标记为此示例添加的元素。

请注意,必须为 System.Net.Http 添加引用。

可以从 异步示例:优化您的应用程序 下载项目。

' Add an Imports directive and a reference for System.Net.Http.
Imports System.Net.Http

' Add the following Imports directive for System.Threading.
Imports System.Threading

Class MainWindow

    ' Declare a System.Threading.CancellationTokenSource.
    Dim cts As CancellationTokenSource

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

        ' Instantiate the CancellationTokenSource.
        cts = New CancellationTokenSource()

        resultsTextBox.Clear()

        Try
            Await AccessTheWebAsync(cts.Token)
            resultsTextBox.Text &= vbCrLf & "Downloads complete."

        Catch ex As OperationCanceledException
            resultsTextBox.Text &= vbCrLf & "Downloads canceled." & vbCrLf

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

        ' Set the CancellationTokenSource to Nothing when the download is complete.
        cts = Nothing
    End Sub

    ' You can still include a Cancel button if you want to.
    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)

        If cts IsNot Nothing Then
            cts.Cancel()
        End If
    End Sub

    ' Provide a parameter for the CancellationToken.
    ' Change the return type to Task because the method has no return statement.
    Async Function AccessTheWebAsync(ct As CancellationToken) As Task

        Dim client As HttpClient = New HttpClient()

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

        ' ***Create a query that, when executed, returns a collection of tasks.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client, ct)

        ' ***Use ToList to execute the query and start the download tasks.
        Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()

        ' ***Add a loop to process the tasks one at a time until none remain.
        While downloadTasks.Count > 0
            ' ***Identify the first task that completes.
            Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)

            ' ***Remove the selected task from the list so that you don't
            ' process it more than once.
            downloadTasks.Remove(finishedTask)

            ' ***Await the first completed task and display the results.
            Dim length = Await finishedTask
            resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
        End While

    End Function

    ' Bundle the processing steps for a website into one async method.
    Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)

        ' GetAsync returns a Task(Of HttpResponseMessage).
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

        Return urlContents.Length
    End Function

    ' Add a method that creates a list of web addresses.
    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

End Class

' Sample output:

' Length of the download:  226093
' Length of the download:  412588
' Length of the download:  175490
' Length of the download:  204890
' Length of the download:  158855
' Length of the download:  145790
' Length of the download:  44908
' Downloads complete.

另请参阅