Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Используя метод Task.WhenAny вместе с CancellationToken, можно отменить все оставшиеся задачи при завершении одной из них. Метод WhenAny
принимает аргумент, который представляет собой коллекцию задач. Метод запускает все задачи и возвращает одну задачу. Одна задача завершается после завершения любой задачи в коллекции.
В этом примере показано, как использовать маркер отмены в сочетании с WhenAny
для удержания первой из завершившихся задач из коллекции задач и отмены оставшихся. Каждая задача загружает содержимое веб-сайта. В примере отображается длина содержимого для завершения первого скачивания, и отменяются другие загрузки.
Замечание
Для выполнения примеров необходимо установить Visual Studio 2012 или более поздней версии и .NET Framework 4.5 или более поздней версии на компьютере.
Скачивание примера
Вы можете скачать полный проект Windows Presentation Foundation (WPF) из примера Async: настройки вашего приложения и затем выполните следующие действия.
Распакуйте скачанный файл и запустите Visual Studio.
В строке меню выберите "Файл", "Открыть", "Проект или решение".
В диалоговом окне "Открыть проект" откройте папку, содержащую пример кода, который вы распаковали, а затем откройте файл решения (.sln) для AsyncFineTuningVB.
В обозревателе решений откройте контекстное меню проекта CancelAfterOneTask , а затем выберите "Задать в качестве проекта запуска".
Выберите клавишу F5 для запуска проекта.
Выберите клавиши CTRL+F5, чтобы запустить проект без отладки.
Запустите программу несколько раз, чтобы убедиться, какая из загрузок завершается первой.
Если вы не хотите скачать проект, просмотрите файл MainWindow.xaml.vb в конце этого раздела.
Создание примера
Пример в этом разделе добавляется в проект, разработанный в разделе "Отмена асинхронной задачи" или "Список задач ", чтобы отменить список задач. В примере используется тот же пользовательский интерфейс, хотя кнопка "Отмена " не используется явно.
Чтобы создать пример самостоятельно, следуйте пошаговым инструкциям в разделе "Скачивание примера", но выберите CancelAListOfTasks в качестве Основного проекта. Добавьте изменения из этой темы в тот проект.
В файле MainWindow.xaml.vb проекта CancelAListOfTasks начните процесс перехода, переместив этапы обработки для каждого веб-сайта из цикла в следующий асинхронный метод.
' ***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
В этом примере используются запрос, метод AccessTheWebAsync
и метод ToArray для создания и запуска массива задач. Приложение массива WhenAny
возвращает одну задачу, которая, когда ожидается, оценивает первую задачу, чтобы достичь завершения в массиве задач.
Внесите следующие изменения в AccessTheWebAsync
. Звездочки помечают изменения в файле кода.
Закомментируйте или удалите цикл.
Создайте запрос, который при выполнении создает коллекцию универсальных задач. Каждый вызов
ProcessURLAsync
возвращает Task<TResult>, гдеTResult
является целым числом.' ***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)
Позвоните по
ToArray
, чтобы выполнить запрос и начать задачи. ПриложениеWhenAny
метода на следующем шаге будет выполнять запрос и запускать задачи без использованияToArray
, но другие методы не могут. Самая безопасная практика — принудительное выполнение запроса явным образом.' ***Use ToArray to execute the query and start the download tasks. Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
Вызов
WhenAny
коллекции задач.WhenAny
возвращает значениеTask(Of Task(Of Integer))
илиTask<Task<int>>
. То есть возвращает задачу,WhenAny
которая оценивается как однаTask(Of Integer)
илиTask<int>
когда она ожидается. Эта одна задача — это первая задача в коллекции, которая будет завершена. Задача, завершиющаяся первым, назначаетсяfinishedTask
. ТипfinishedTask
— это Task<TResult>, гдеTResult
является целым числом, потому что это возвращаемый типProcessURLAsync
.' ***Call WhenAny and then await the result. The task that finishes ' first is assigned to finishedTask. Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
В этом примере вас интересует только задача, которая завершается первой. Поэтому используйте CancellationTokenSource.Cancel для отмены оставшихся задач.
' ***Cancel the rest of the downloads. You just want the first one. cts.Cancel()
Наконец, дождитесь
finishedTask
, чтобы получить длину загруженного содержимого.Dim length = Await finishedTask resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website: {length}" & vbCrLf
Запустите программу несколько раз, чтобы убедиться, какая из загрузок завершается первой.
Полный пример
Следующий код представляет собой полный файл MainWindow.xaml.vb или MainWindow.xaml.cs для примера. Звездочки помечают элементы, добавленные в этот пример.
Обратите внимание, что необходимо добавить ссылку для 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 & "Download complete."
Catch ex As OperationCanceledException
resultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf
Catch ex As Exception
resultsTextBox.Text &= vbCrLf & "Download 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()
'' Comment out or delete the loop.
''For Each url In urlList
'' ' GetAsync returns a Task(Of HttpResponseMessage).
'' ' Argument ct carries the message if the Cancel button is chosen.
'' ' Note that the Cancel button can cancel all remaining downloads.
'' Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)
'' ' Retrieve the website contents from the HttpResponseMessage.
'' Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()
'' resultsTextBox.Text &=
'' vbCrLf & $"Length of the downloaded string: {urlContents.Length}." & vbCrLf
''Next
' ***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 ToArray to execute the query and start the download tasks.
Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
' ***Call WhenAny and then await the result. The task that finishes
' first is assigned to finishedTask.
Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
' ***Cancel the rest of the downloads. You just want the first one.
cts.Cancel()
' ***Await the first completed task and display the results
' Run the program several times to demonstrate that different
' websites can finish first.
Dim length = Await finishedTask
resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website: {length}" & vbCrLf
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 downloaded website: 158856
' Download complete.