Страницы

Поиск по вопросам

пятница, 12 апреля 2019 г.

Чем чревато отсутствие обработки OperationCanceledException у Task?

Чем может быть чреват такой вот Task с необработанным исключением отмены действия, если далее я к нему нигде не обращаюсь?
public static void Main() { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); Task.Run(() => SomeMethod(cts.Token), cts.Token); }
public static void SomeMethod(CancellationToken token) { while (true) { token.ThrowIfCancellationRequested(); // Некая трудоемкая операция Thread.Sleep(TimeSpan.FromMilliseconds(100)); } }
UPDATE1: После ответа andreycha я решил проверить, как работает ловля ошибок у Task. И я заметил, что исключение отмены не доходит до глобального обработчика и никак не вешает систему. В кофиг файле прописал:

Код:
public static void Main() { TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => { Console.WriteLine("Task error"); eventArgs.SetObserved(); (eventArgs.Exception).Handle(ex => { Console.WriteLine("Exception type: " + ex.GetType()); return true; }); };
CancellationTokenSource cts = new CancellationTokenSource(1000); Task.Factory.StartNew(() => { Console.WriteLine("Enter"); while (true) { cts.Token.ThrowIfCancellationRequested(); Thread.Sleep(100); } }, cts.Token); Task.Factory.StartNew(() => { throw new Exception("Some exception"); });
Thread.Sleep(4000); Console.WriteLine("Collecting"); GC.Collect(); GC.WaitForPendingFinalizers();
Console.ReadLine(); }
Пример вывода:
Task error Exception type: System.Exception
Выходит, что если тебе никак не надо обработать отмену операции, то можно в обще ничего не делать и ничего не будет все таки?


Ответ

Если приложение работает под .NET Framework 4 (или под .NET Framework 4.5+ с включенной опцией ThrowUnobservedTaskExceptions), то когда сборщик мусора доберется до этого таска, финализатор выбросит исключение и приложение упадет.
В .NET Framework 4.5+ поведение изменили и приложение продолжит работать (а само исключение по-прежнему можно отловить в обработчике UnobservedTaskException). Необработанное исключение ничему не помешает.

Однако я бы рекомендовал всегда обзервить таски, иначе вы не узнаете, завершился ли таск успешно или нет и завершился ли вообще. В 99% случаев unobserved task -- это ошибка. Отследить завершение можно разными способами (зависит от вашей текущей архитектуры по большей степени):
1) Если вы уже используете async/await, тогда ожидайте так:
var task = Task.Run(() => SomeMethod(cts.Token), cts.Token); try { await task; } catch (OperationCanceledException) { // задача была отменена } catch (Exception) { // другая ошибка }
2) Если ваш код полностью синхронный, то можно использовать либо продолжения, либо синхронное ожидание.
Вариант с продолжением. Помните о том, что продолжение выполняется в том же контексте, что и оригинальный таск (т.е. в потоке из пула потоков). А значит обращаться напрямую к компонентам UI, например, нельзя.
Task.Run(() => SomeMethod(cts.Token), cts.Token) .ContinueWith(SomeMethodHandler, TaskContinuationOption.OnlyOnFaulted); ... private void SomeMethodHandler(Task task) { if (task.Exception is OperationCanceledException) { // задача была отменена } else { // другая ошибка } }
Вариант с синхронным ожиданием. Тут надо быть аккуратным с тем, в каком конкретно месте вы ожидаете. Поскольку внутри SomeMethod у вас бесконечный цикл, то на строке task.Wait() приложение будет висеть до тех пор, пока задача не будет отменена
var task = Task.Run(() => SomeMethod(cts.Token), cts.Token); try { task.Wait(); } catch (AggregateException e) { // синхронное ожидание, в отличие от await, не "разворачивает" исключения // проверяем e.InnerExceptions на предмет наличия OperationCanceledException }

P.S. Если же говорить о коде, который вы привели, то он завершит свое выполнение почти моментально, не успев произвести нужную работу. Потому что таск никто не ожидает.

Комментариев нет:

Отправить комментарий