Я хочу подождать, пока Задание <T> завершится с некоторыми специальными правилами: если оно не завершилось через X миллисекунд, я хочу отобразить сообщение для пользователя. И если он не завершился через Y миллисекунд, я хочу автоматически запросить отмену .
Я могу использовать Task.ContinueWith для асинхронного ожидания завершения задачи (т. Е. Запланировать действие, которое будет выполнено по завершении задачи), но это не позволяет указать время ожидания. Я могу использовать Task.Wait для синхронного ожидания завершения задачи с таймаутом, но это блокирует мой поток. Как я могу асинхронно ожидать завершения задачи с тайм-аутом?
CancellationTokenSource
. Доступны две перегрузки для конструктора, одна из которых принимает целочисленную миллисекундную задержку, а другая - задержку TimeSpan.Ответы:
Как насчет этого:
А вот отличное сообщение в блоге «Создание метода Task.TimeoutAfter» (от команды MS Parallel Library) с дополнительной информацией о подобных вещах .
Дополнение : по запросу комментария к моему ответу здесь представлено расширенное решение, включающее обработку отмены. Обратите внимание, что передача отмены в задачу и таймер означает, что в вашем коде может быть несколько способов отмены, и вы должны обязательно проверить и убедиться, что вы правильно обрабатываете все из них. Не оставляйте на волю различные комбинации и надейтесь, что ваш компьютер правильно работает во время выполнения.
источник
Task.Delay
задание поддерживается системным таймером, который будет отслеживаться до истечения времени ожидания независимо от того, сколько времениSomeOperationAsync
занимает. Таким образом, если весь этот фрагмент кода выполняется много раз в узком цикле, вы расходуете системные ресурсы на таймеры, пока они не прекратят работу. Способ исправить это состоит в том,CancellationToken
что вы передадите емуTask.Delay(timeout, cancellationToken)
то, что отмените, когдаSomeOperationAsync
закончите, чтобы освободить ресурс таймера.Task
, что любое исключение, сохраненное задачей, перебрасывается в этот момент. Это дает вам шанс пойматьOperationCanceledException
(если отменено) или любое другое исключение (если отказано).Task.Wait(timeout)
будет синхронно блокировать вместо асинхронного ожидания.Вот версия метода расширения, которая включает в себя отмену тайм-аута, когда исходное задание завершается, как это было предложено Эндрю Арноттом в комментарии к его ответу .
источник
using
блокеtask.Result
когда выполнено дважды.task
) все еще продолжаться в случае тайм-аута?TimeoutException
есть подходящее сообщение по умолчанию. Переопределение с помощью «Время операции истекло». не добавляет никакой ценности и фактически вызывает некоторую путаницу, подразумевая, что есть причина переопределить это.Вы можете использовать
Task.WaitAny
для ожидания первой из нескольких задач.Вы можете создать две дополнительные задачи (которые будут выполнены после заданного времени
WaitAny
ожидания ), а затем использовать их для ожидания того, что завершится первым. Если задача, выполненная первой, является вашей «рабочей» задачей, то вы закончили. Если задача, выполненная первой, является задачей тайм-аута, вы можете отреагировать на тайм-аут (например, отмена запроса).источник
below
.... что это? на основе ТАКОГО заказа?Как насчет этого?
Вы можете использовать опцию Task.Wait, не блокируя основной поток, используя другую задачу.
источник
Вот полностью проработанный пример, основанный на ответе с наибольшим количеством голосов:
Основным преимуществом реализации в этом ответе является то, что дженерики были добавлены, поэтому функция (или задача) может возвращать значение. Это означает, что любая существующая функция может быть включена в функцию тайм-аута, например:
Перед:
После:
Этот код требует .NET 4.5.
Предостережения
Получив этот ответ, обычно не рекомендуется создавать исключения в коде во время нормальной работы, если только вам абсолютно не нужно:
Используйте этот код только в том случае, если вы абсолютно не можете изменить вызываемую функцию, поэтому время ожидания истекает после определенного
TimeSpan
.Этот ответ действительно применим только при работе со сторонними библиотечными библиотеками, в которых просто невозможно изменить рефакторинг для включения параметра времени ожидания.
Как написать надежный код
Если вы хотите написать надежный код, общее правило таково:
Если вы не соблюдаете это правило, ваш код в конце концов столкнется с операцией, которая по какой-то причине завершится неудачей, затем он будет блокироваться на неопределенный срок, а ваше приложение просто навсегда зависло.
Если через какое-то время произошел разумный тайм-аут, то ваше приложение зависло бы в течение некоторого экстремального периода времени (например, 30 секунд), затем оно либо отобразило бы ошибку и продолжило бы работать, либо повторило попытку.
источник
Используя превосходную библиотеку AsyncEx Стивена Клири , вы можете сделать:
TaskCanceledException
будет брошено в случае тайм-аута.источник
Это слегка улучшенная версия предыдущих ответов.
CancellationToken
исходное задание, а когда истечет время ожидания, вы получитеTimeoutException
вместоOperationCanceledException
.Применение
InnerCallAsync
может занять много времени, чтобы завершить.CallAsync
оборачивает это тайм-аутом.источник
timeoutCancellation
вdelayTask
. В настоящее время, если вы запускаете отмену,CancelAfterAsync
можете броситьTimeoutException
вместоTaskCanceledException
, причинаdelayTask
может закончиться первой.WhenAny
одним и тем же токеном,WhenAny
вернут первую задачу. Это предположение было неверным. Я отредактировал ответ. Спасибо!Используйте таймер для обработки сообщения и автоматической отмены. Когда Задача завершится, вызовите Dispose на таймерах, чтобы они никогда не сработали. Вот пример; измените taskDelay на 500, 1500 или 2500, чтобы увидеть разные случаи:
Кроме того, Async CTP предоставляет метод TaskEx.Delay, который обернет таймеры в задачи для вас. Это может дать вам больше контроля над такими вещами, как установка TaskScheduler для продолжения при срабатывании таймера.
источник
task.Wait()
.Другой способ решения этой проблемы - использование Reactive Extensions:
Проверьте выше, используя приведенный ниже код в вашем модульном тесте, он работает для меня
Вам может понадобиться следующее пространство имен:
источник
Общая версия ответа @ Kevan выше с использованием Reactive Extensions.
С дополнительным планировщиком:
Кстати: когда тайм-аут происходит, исключение тайм-аут
источник
Если вы используете BlockingCollection для планирования задачи, производитель может запустить потенциально долго выполняющуюся задачу, а потребитель может использовать метод TryTake, в который встроен маркер времени ожидания и отмены.
источник
Я почувствовал
Task.Delay()
задачу иCancellationTokenSource
в других ответах немного для моего варианта использования в тесном сетевом цикле.И хотя метод Джо Хоага «Создание задачи». TimeoutAfter в блогах MSDN был вдохновляющим, я немного устал от использования
TimeoutException
управления потоком по той же причине, что и выше, потому что время ожидания ожидается чаще, чем нет.Итак, я пошел с этим, который также обрабатывает оптимизации, упомянутые в блоге:
Пример использования выглядит так:
источник
Несколько вариантов ответа Эндрю Арнотта:
Если вы хотите подождать существующую задачу и выяснить, завершена ли она или истекло время ожидания, но не хотите отменять ее, если истекло время ожидания:
Если вы хотите запустить рабочую задачу и отменить ее, если истекло время ожидания:
Если у вас уже есть задача, которую вы хотите отменить в случае истечения времени ожидания:
Еще один комментарий, эти версии будут отменять таймер, если тайм-аут не происходит, поэтому несколько вызовов не приведет к накоплению таймеров.
SJB
источник
Я рекомендую идеи некоторых других ответов здесь и этот ответ в другом потоке в метод расширения Try-style. Это имеет преимущество, если вы хотите метод расширения, но избегаете исключения по таймауту.
источник
Определенно не делайте этого, но это вариант, если ... я не могу придумать вескую причину.
источник