不检查是否已取消的 TPL 任务是否仍可取消?

Can a TPL task that doesn't check whether it's cancelled, still be cancelled?

我认为 CancellationToken/CancellationTokenSource 系统工作起来有点像 C++ volatile bool bFlagCancelled,这意味着取消是任务自愿的,依赖于任务本身不时检查时间是否取消并抛出异常,显式或通过调用 ThrowIfCancellationRequested().

但是如果我在 StartNew() 之后立即调用 Cancel,任务就会停止,并且调用 Wait() 会抛出一个 TaskCanceledException.

例如,对于此代码:

CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
Task task = Task.Factory.StartNew(
    () =>
    {
        Console.WriteLine("start sleep");
        Thread.Sleep(1000);
        Console.WriteLine("sleep ended");
    }
    , token);
// Thread.Sleep(1);
source.Cancel();
Console.WriteLine("start wait");
task.Wait();
Console.WriteLine("wait ended");

我得到这个输出:

start wait
Exception: System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled.

但是如果我取消注释 // Thread.Sleep(1);,那么行为就会改变,我会得到这个输出:

start sleep
start wait
sleep ended
wait ended

现在我想也许这是因为尚未调用 task.Start(),但据我了解,StartNew() 在返回之前调用了 task.Start() - 这就是它没有调用的原因有同步成本,建议不要创建 new Task 并自己调用 task.Start()

那么这意味着在某些情况下,任务会自发取消,即使没有检查取消标记。这是发生这种情况的唯一情况,还是有更多情况发生这种情况?

Task.Factory.StartNew在返回之前调度任务,但并不意味着任务真正开始执行。所以还有取消任务的空间,如果 TaskScheduler 接受它(在内部,调用 TaskScheduler.TryDequeue 方法。如果 returns 为真,任务可以被标记为已取消)。 =14=]