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

13

我看到眼前的这段代码,感到很可疑:

CancellationTokenSource _cts;

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

在取消操作后立即调用_cts.Dispose()是否安全?这样做会否导致底层资源的释放,而这些资源对于被取消的任务成功Wait CancellationToken是必需的,如果它想这样做的话?

1个回答

17

在取消操作后直接调用_cts.Dispose()安全吗?

为了回答这个问题,我们需要了解当我们取消一个CancellationTokenSource时会发生什么。

当你取消一个CancellationTokenSource时,它会继续通过CancellationToken.Register()方法调用任何已注册的回调函数,该函数通过CancellationToken引用其父源。

当你处置CTS时,将尝试从令牌中注销任何链接回调。如果当前正在执行,它将等待其委托完成。

这意味着,尽管您已处置了CTS,但它的对象仍然被令牌引用。因此,它仍然不具备收集资格。

现在让我们看看CancellationToken.IsCancellationRequested

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

这意味着,在处理后,检查取消标记将返回 true。也就是说,在调用 dispose 后,等待任务完成是安全的。

顺便提一下,如果您(出于某种原因)尝试通过其已处理的 CancellationTokenSource 传递令牌,则会遇到 ObjectDisposedException

编辑:

我想要补充两件事情。首先,我不建议使用此方法。它应该适用于某些代码执行路径,但不是全部。只有在使用其 WaitHandle 属性时才应该正常处理 CancellationTokenSource。否则,可以让 GC 完成清理。但是,由于这是一种口味问题,您可以选择任何一种方式。我强烈建议只在确保任务已观察到取消请求后再处理。

关于使用 WaitHandle,它将在您处理后被处理并置空,因此 将不可访问


2
但是IsCancellationRequested并不是唯一需要担心的情况,对吧?WaitHandle呢? - Tim Lovell-Smith
如果运行了dispose,WaitHandle将被关闭。我不确定使用WaitHandle是否是常见用例。我会在我的答案中添加这一点。 - Yuval Itzchakov
@Tim 我已经对我的回答进行了重要的编辑。虽然你想做的是可能的,但我肯定不建议这样做。 - Yuval Itzchakov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接