#c_sharp #многопоточность #tpl
Чем может быть чреват такой вот 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 Выходит, что если тебе никак не надо обработать отмену операции, то можно в обще ничего не делать и ничего не будет все таки?
Ответы
Ответ 1
Если приложение работает под .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. Если же говорить о коде, который вы привели, то он завершит свое выполнение почти моментально, не успев произвести нужную работу. Потому что таск никто не ожидает.Ответ 2
Если использовать библиотеку NLog - то там у класса Logger есть метод SwallowAsync, который позволяет залогировать асинхронную ошибку если она вдруг возникла. Впрочем, стандартная реализация тут в любом случае не подойдет, ведь отмена задачи - нормальный способ окончания работы, а не ошибочный. В данном случае, когда весь код свой, я бы предпочел не создавать себе проблем вместо того чтобы их героически решать: public static void SomeMethod(CancellationToken token) { while (true) { if (token.IsCancellationRequested) return; // Некая трудоемкая операция Thread.Sleep(TimeSpan.FromMilliseconds(100)); } }
Комментариев нет:
Отправить комментарий