#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)); } }
Комментариев нет:
Отправить комментарий