为什么CancellationTokenRegistration存在,为什么它实现IDisposable

i3a*_*non 18 .net c# idisposable task-parallel-library cancellation-token

我一直在看到Cancellation.Register与结果中的using子句一起使用的代码CancellationTokenRegistration:

using (CancellationTokenRegistration ctr = token.Register(() => wc.CancelAsync()))
{
    await wc.DownloadStringAsync(new Uri("http://www.hamster.com"));
}
Run Code Online (Sandbox Code Playgroud)

我得到你应该确保你DisposeIDisposable,但为什么它甚至工具IDisposable?有什么资源需要发布?它认为平等的唯一方法.

如果你不这样做Dispose会怎么样?你泄漏了什么?

nos*_*tio 16

此模式是确保CancellationTokenRegistration.Unregister()自动调用的便捷方式.它经常被Stephen Toub用于.NET博客文章的并行编程,例如这里.

我知道你应该确保你处理一个IDisposable,但为什么它甚至实现IDisposable?有什么资源需要发布?它认为平等的唯一方法.

IMO,对此的最佳答案可以在微软Mike Liddell 的.NET 4取消框架文章中找到:

当向a注册回调时,将捕获CancellationToken当前线程,ExecutionContext以便使用完全相同的安全上下文运行回调.ct.Register()如果需要,可以通过过载来请求捕获当前线程的同步上下文是可选的.回调通常被存储,然后被要求注销时运行,但如果回调取消已要求注册后,回调将立即在当前线程上运行,或通过Send()对当前 SynchronizationContext(如果适用).

当回调被注册到a时CancellationToken,返回的对象是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
Run Code Online (Sandbox Code Playgroud)

因此,对于一次性CancellationTokenRegistration使用using(或CancellationTokenRegistration.Dispose()明确地使用try/finally)的范围是重要的.