Поделиться через


Запуск нескольких асинхронных задач и их обработка по мере их завершения (Visual Basic)

С помощью Task.WhenAnyможно одновременно запускать несколько задач и обрабатывать их по одному, а не обрабатывать их в том порядке, в котором они запущены.

В следующем примере используется запрос для создания коллекции задач. Каждая задача скачивает содержимое указанного веб-сайта. В каждой итерации цикла во время ожидания вызов WhenAny возвращает задачу в коллекции задач, завершающих загрузку первым. Эта задача удаляется из коллекции и затем обрабатывается. Цикл повторяется, пока коллекция не содержит больше задач.

Замечание

Для выполнения примеров необходимо установить Visual Studio 2012 или более поздней версии и .NET Framework 4.5 или более поздней версии на компьютере.

Скачивание примера

Вы можете скачать полный проект Windows Presentation Foundation (WPF) из примера Async: настройки вашего приложения и затем выполните следующие действия.

  1. Распакуйте скачанный файл и запустите Visual Studio.

  2. В строке меню выберите "Файл", "Открыть", "Проект или решение".

  3. В диалоговом окне "Открыть проект" откройте папку, содержащую пример кода, который вы распаковали, а затем откройте файл решения (.sln) для AsyncFineTuningVB.

  4. В обозревателе решений откройте контекстное меню для проекта ProcessTasksAsTheyFinish , а затем выберите "Задать в качестве проекта запуска".

  5. Выберите клавишу F5 для запуска проекта.

    Выберите клавиши CTRL+F5, чтобы запустить проект без отладки.

  6. Запустите проект несколько раз, чтобы убедиться, что скачанные длины не всегда отображаются в одном порядке.

Если вы не хотите скачать проект, просмотрите файл MainWindow.xaml.vb в конце этого раздела.

Создание примера

В этом примере добавляется код к разработанному в разделе "Отмена оставшихся асинхронных задач после завершения одной задачи (Visual Basic)" и используется тот же пользовательский интерфейс.

Чтобы создать пример самостоятельно, следуйте пошаговым инструкциям в разделе "Загрузка примера", но выберите CancelAfterOneTask в качестве StartUp Project. Добавьте изменения в данную тему в метод 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, как описано в примере, для решения проблем, связанных с небольшим количеством задач. Однако другие подходы более эффективны при наличии большого количества задач для обработки. Дополнительные сведения и примеры см. в разделе "Обработка задач по мере их выполнения".

Полный пример

Следующий код представляет собой полный текст файла MainWindow.xaml.vb для примера. Звездочки помечают элементы, добавленные в этот пример.

Обратите внимание, что необходимо добавить ссылку для System.Net.Http.

Проект можно скачать из примера Async: тонкая настройка вашего приложения.

' 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.  

См. также