Чем может быть чреват такой вот 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. Если же говорить о коде, который вы привели, то он завершит свое выполнение почти моментально, не успев произвести нужную работу. Потому что таск никто не ожидает.
Комментариев нет:
Отправить комментарий