Делаю:
Task.Factory.StartNew(() =>Method(),ct)
В другом месте вызываю tokenSource.Cancel();
, но ничего не происходит.
Мне казалось, что такая перегрузка должна полностью задачу снимать.
Получается, что нужно токен передавать непосредственно в сам метод и там производить анализ?
Можно ли как-нибудь отменить задачу, непередавая во внутрь токен?
Смотрите.
Вы не можете «отменить» уже бегущий код — по тем же причинам, по которым вы не можете «убить» бегущий thread. Поэтому код, запускаемый через Task.Run
, добежит до конца, если только он сам не будет анализировать токен и не отменит себя сам.
Однако, таск не будет запущен, если токен находится уже в отменённом состоянии. Поскольку запуск Task
'а — расходная штука, это неплохая оптимизация.
Кроме того, таск при этом будет ассоциирован с этим токеном, и если код в Method
бросит исключение через token.ThrowIfCancellationRequested
, это исключение будет считаться относящимся к таску. Тем самым таск будет завершён в состоянии Cancelled
, а не Faulted
.
Источник информации: https://social.msdn.microsoft.com/Forums/en-US/c2f614f6-c96c-4821-84cc-050b21aaee45/taskfactorystartnew-cancellation-token-parameter?forum=parallelextensions
Воспроизводящий пример:
async Task Run()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
var t = Task.Run((Action)(() =>
{
while (true)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(200);
}
}), cts.Token);
await Task.Delay(400);
cts.Cancel();
try
{
await t;
}
catch (TaskCanceledException ex)
{
Console.WriteLine($"caught TaskCanceledException, task status = {t.Status}");
}
catch (OperationCanceledException ex)
{
Console.WriteLine($"caught OperationCanceledException, task status = {t.Status}");
}
}
Этот код попадает в catch (OperationCanceledException ex)
, и состояние таска — Canceled
. А если убрать токен, то мы попадаем также в catch (OperationCanceledException ex)
, но состояние — Faulted
. В этом случае рантайм считает, что произошла не отмена таска, а просто какое-то постороннее исключение.
(В TaskCanceledException
мы попадаем, если таск был отменён до запуска.)
(Ошибка в предыдущей версии кода была в том, что не было явного приведения к Action
[которое практически никогда не нужно!], и ввиду бесконечного цикла внутри типовыводитель не мог решить, это перегрузка с Action
или перегрузка с Func<Task>
! В результате он выбирал не то, что нужно, и рантайм считал, что отменён не таск, а процесс его создания.)
Да, токен необходимо передавать в метод, и там периодически вызывать ThrowIfCancellationRequested()
. Однако, это не отменяет необходимости передавать его вторым параметром StartNew()
. Это делается на тот случай, если отмена операции запрошена раньше чем начал выполняться ваш метод (а так же для того, чтобы связать таск с токеном). Примерно так:
var cts = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
while (true)
{
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("I'm working...");
}
}, cts.Token);
cts.CancelAfter(10);
Если была запрошена отмена, то метод ThrowIfCancellationRequested()
кинет OperationCanceledException
. При желании, его можно поймать и сообщить юзеру, что операция успешно отменена, например.
Если хочется, то можно обходиться без эксепшена, а просто в методе проверять:
if (token.IsCancellationRequested)
{
/*выходим из метода*/
}
Но тогда сама задача не будет знать о том, что её отменили. В свойстве Status
у неё будет TaskStatus.RanToCompletion
вместо TaskStatus.Canceled
.
UPD: Дополнительный материал по теме: https://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Добрый день, столкнулся со следующей проблемкойНе получается задать прозрачность ComboBoxItem
Доброго времени сутокРазрабатывал приложение на мониторе 4:3 и сейчас купил 16:9
Есть некоторое WCF приложениеВ контракте на хосте WCF есть такой метод: