在任务取消时处理 CancellationTokenSource 的代码是否正确?

Is code that disposes CancellationTokenSource while tasks are canceling correct?

我看到眼前这段代码,心存疑虑:

CancellationTokenSource _cts;

public void Dispose();
{
    _cts.Cancel();
    _cts.Dispose();
    _task.Wait(); //wait for the task to be canceled!?
}

取消后直接调用 _cts.Dispose() 是否安全?是否会处理被取消任务所需的 CancellationTokenSource 基础资源以成功等待 CancellationToken(如果它想要这样做)?

Is it safe to call _cts.Dispose() straight after cancel?

为了知道这一点,我们需要了解取消 CancellationTokenSource 时会发生什么。

当您取消 CancellationTokenSource 时,它会继续调用通过 CancellationToken 注册的任何回调,它通过 CancellationToken.Register() 方法持有对其父源的引用。

当您处理 CTS 时,任何已注册的链接回调都会尝试从令牌中注销。如果它当前正在执行,它将等到它的委托完成。

这意味着,尽管您已经处置了 CTS,但它的对象仍由令牌引用。因此,它仍然不符合收集条件。

现在让我们看看CancellationToken.IsCancellationRequested:

public bool IsCancellationRequested 
{
    get
    {
        return m_source != null && m_source.IsCancellationRequested;
    }
}

这意味着在处置时,检查取消将产生 true。这意味着,调用 dispose 后等待任务完成是安全的。

附带说明一下,如果您(出于某种原因)尝试通过已处置的令牌传递令牌 CancellationTokenSource,您将遇到 ObjectDisposedException

编辑:

我想补充两件事。首先,让我说我不推荐使用这种方法。它应该适用于某些代码执行路径,但不适用于所有路径。 CancellationTokenSource 通常只有在您使用 WaitHandle 属性 时才应该被丢弃。否则,将其留给 GC 进行清理就可以了。但是,由于这是口味问题,您可以选择您喜欢的任何一个。我当然建议仅在您确定任务已遵守取消请求后才进行处置。

根据 WaitHandle 的用法,它会被释放并在你释放后被清空,所以它 将无法 访问。