Ответ 1
Я считаю, что это ожидаемое поведение, потому что вы работаете с изменением состояния гонки.
От Как отменить задачу и ее детей:
вызывающий поток не принудительно завершает задачу; он только сигнализирует о том, что требуется аннулирование. Если задача уже запущена, делегат пользователя должен заметить запрос и ответить соответствующим образом. Если запрос аннулирования запрашивается перед запуском задачи, делегат пользователя никогда не выполняется и объект задачи переходит в состояние
Canceled
.
Вы можете завершить операцию с помощью [...], просто возвращаясь из делегата. Во многих сценариях этого достаточно; однако экземпляр задачи, который "отменяется" таким образом, переходит в состояние
RanToCompletion
, а не в состояниеCanceled
.
Моя образованная догадка заключается в том, что, когда вы вызываете .Start()
в своих двух задачах, возможно, что один (или оба они) на самом деле не запускались, прежде чем вы набрали .Cancel()
на CancellationTokenSource
. Бьюсь об заклад, если вы ставите хотя бы три секунды ожидания между началом заданий и отменой, это не вызовет исключения. Кроме того, вы можете проверить свойство .Status
для обеих задач. Если я прав, свойство .Status
должно читать TaskStatus.Canceled
по крайней мере для одного из них при вызове исключения.
Помните, что запуск нового Task
не гарантирует создание нового потока. TPL принимает решение о том, что получает новый поток и что просто поставлено в очередь для выполнения.