Основное различие между вашими задачами является перегрузка для Task.Run
метода, который вы используете:
task1
создается с Task.Run Method (Func<Task>)
, а не task2
создается с Task.Run<TResult> Method (Func<TResult>)
. Это перегружает ли создать задачу с небольшой разницей битной:
- для
task1
свойство Result
устанавливаются в System.Threading.Tasks.VoidTaskResult
и CreationOptions
установлен в None
,
- , а не
task2
CreationOptions
установлен в DenyChildAttach
, и результат представляет собой default(int)
, который составляет 0
.
Когда вы ждете в task2
, свойство Result
не быть установлен в реальной стоимости, так как исключение. По MSDN:
Когда экземпляр задачи наблюдает в OperationCanceledException
брошенного коды пользователя, он сравнивает маркер, за исключением к связанному с ним лексемами (тот, который был передан в API, который создал Task
). Если они одинаковы и IsCancellationRequested
свойства лексемы возвращает истинного, задача интерпретирует это как признание отмены и переходов в Canceled
состояния. Если вы не используете метод Wait
или WaitAll
для ожидания задания, задача просто устанавливает свой статус на Canceled
.
Если вы ждете на Задаче, переходящую в Canceled
состояния, System.Threading.Tasks.TaskCanceledException
исключения (завернутые в AggregateException
исключения) выбрасываются. Примечание , что это исключение указывает на успешную отмену, а не на неисправную ситуацию . Поэтому задание Exception
возвращает имущество null.
Если IsCancellationRequested
свойства лексемы возвращает false
или если маркер за исключением не соответствует Целевому маркеру, то OperationCanceledException
трактуются как обычное исключение, вызывая задачу перехода к Faulted
состояния.Также обратите внимание, что наличие других исключений также приведет к переходу Task
к состоянию Faulted
. Статус завершенной задачи можно получить в Status
.
Итак, здесь мы можем найти причину такого поведения - исключение рассматривается как нормальное исключение из-за несоответствия токенов. Это странно, потому что токен definitely the same (я проверил, что в Debug хеш-код равен Equals
метод и оператор double equals возвращает true
), но сравнение все равно возвращает false
. Таким образом, решение для Вашего случая является явное использование в cancellation tokens, что-то вроде этого (я добавил Thread.Sleep
, чтобы избежать состояния гонки):
var t1TokenSource = new CancellationTokenSource();
var t1 = Task.Run(() =>
{
Thread.Sleep(1000);
if (t1TokenSource.Token.IsCancellationRequested)
{
t1TokenSource.Token.ThrowIfCancellationRequested();
}
//throw new TaskCanceledException();
}, t1TokenSource.Token);
try
{
t1TokenSource.Cancel();
t1.Wait();
}
catch (AggregateException e)
{
Debug.Assert(t1.IsCanceled);
}
var t2TokenSource = new CancellationTokenSource();
var t2 = Task.Run(() =>
{
Thread.Sleep(1000);
if (t2TokenSource.Token.IsCancellationRequested)
{
t2TokenSource.Token.ThrowIfCancellationRequested();
}
//throw new TaskCanceledException();
return 1;
}, t2TokenSource.Token);
try
{
t2TokenSource.Cancel();
t2.Wait();
}
catch (AggregateException e)
{
Debug.Assert(t2.IsCanceled);
}
Другая цитата из MSDN:
Вы может прекратить операцию, используя один из следующих вариантов:
- Просто вернувшись от делегата. Во многих сценариях этого достаточно; однако экземпляр задачи, который отменяется таким образом, переходит в состояние
TaskStatus.RanToCompletion
, а не в состояние TaskStatus.Canceled
.
- Отбрасывая
OperationCanceledException
и передавая ему токен, по которому была запрошена отмена. Предпочтительный способ сделать это: , чтобы использовать метод ThrowIfCancellationRequested
. Задача, которая отменена таким образом, переходит в состояние Canceled
, которое вызывающий код может использовать для проверки того, что задача ответила на его запрос на отмену .
Как вы можете видеть, Предпочитаемый способ работает предсказуемо, прямой бросок исключения не делает. Также обратите внимание, что в случае использования task
также создается с DenyChildAttach
и не имеет свойства Result
, поэтому есть некоторая разница в конструкторах, с которыми вы столкнулись.
Надеюсь, это поможет.