此模式是确保CancellationTokenRegistration.Unregister()
自动调用的便捷方式。Stephen Toub 在他的Parallel Programming with .NET博客文章中经常使用它,例如这里。
我知道你应该确保你 Dispose 一个 IDisposable,但为什么它甚至实现了 IDisposable?它必须释放哪些资源?它唯一的方法是关于平等。
IMO,对此的最佳答案可以在Microsoft 的 Mike Liddell的.NET 4 Cancellation Framework帖子中找到:
当回调注册到 aCancellationToken
时,当前线程ExecutionContext
被捕获,以便回调将使用完全相同的安全上下文运行。当前线程的同步上下文的捕获是可选的,ct.Register()
如果需要,可以通过重载来请求。回调通常被存储,然后在请求取消时运行,但如果在请求取消后注册回调,则回调将立即在当前线程上运行,或者如果适用,则在当前线程Send()
上
运行。SynchronizationContext
当回调注册到 aCancellationToken
时,返回的对象是 a CancellationTokenRegistration
。这是一个轻量级的结构类型,IDiposable
处理这个注册对象会导致回调被注销。保证在
Dispose()
方法返回后,注册的回调既不会运行也不会随后开始。这样做的结果是,
CancellationTokenRegistration.Dispose()
如果回调当前正在执行,则必须阻塞。因此,所有注册的回调都应该是快速的,并且不会阻塞很长时间。
Mike Liddell 的另一个相关文档是“在 .NET Framework 4 中使用取消支持”(UsingCancellationinNET4.pdf)。
更新,这在参考源中是可验证的。
同样重要的是要注意,取消回调是用 注册的CancellationTokenSource
,而不是用CancellationToken
. 因此,如果CancellationTokenRegistration.Dispose()
范围不正确,注册将在父CancellationTokenSource
对象的生命周期内保持活动状态。当异步操作的范围结束时,这可能会导致意外回调,例如:
async Task TestAsync(WebClient wc, CancellationToken token)
{
token.Register(() => wc.CancelAsync());
await wc.DownloadStringAsync(new Uri("http://www.hamster.com"));
}
// CancellationTokenSource.Cancel() may still get called later,
// in which case wc.CancelAsync() will be invoked too
因此,将一次性CancellationTokenRegistration
使用的范围限定为using
(或CancellationTokenRegistration.Dispose()
显式调用try/finally
)很重要。