我一直在Cancellation.Register
结果中看到使用using
和CancellationTokenRegistration
子句的代码:
using (CancellationTokenRegistration ctr = token.Register(() => wc.CancelAsync()))
{
await wc.DownloadStringAsync(new Uri("http://www.hamster.com"));
}
我知道你应该确保Dispose
IDisposable
,但为什么它会实现IDisposable
?有什么资源需要发布?它只考虑平等的唯一方法。
如果你没有Dispose
会怎么样?你泄漏了什么?
答案 0 :(得分:16)
此模式是确保自动调用CancellationTokenRegistration.Unregister()
的便捷方式。 Stephen Toub经常在他的Parallel Programming with .NET博文中使用它,例如: here
我知道你应该确保你处理一个IDisposable,但为什么 它甚至实现了IDisposable?它有什么资源 发布?它只考虑平等的唯一方法。
IMO,对此的最佳答案可以在微软Mike Liddell的.NET 4 Cancellation Framework帖子中找到:
当回调注册到
CancellationToken
时,当前 捕获线程的ExecutionContext
以便运行回调 与完全相同的安全上下文。捕获的 可以请求当前线程的同步上下文是可选的 如果需要,通过ct.Register()
的重载。回调通常是 存储然后在请求取消时运行,但是如果是回调 已经请求取消后注册,回调将 立即在当前线程上运行,或通过当前线程上的Send()
运行SynchronizationContext
如果适用的话。当回调注册到
CancellationToken
时,返回 对象是CancellationTokenRegistration
。这是一种轻型结构类型 即IDiposable
,并且处理此注册对象会导致 回调要注销。保证在之后Dispose()
方法已返回,注册的回调都不是 跑步也不会随后开始。这样做的结果是 如果回调是CancellationTokenRegistration.Dispose()
必须阻止 目前正在执行因此,所有注册的回调都应该很快 并且不会阻止任何重要的持续时间。
Mike Liddell的另一份相关文件是"Using Cancellation Support in .NET Framework 4" (UsingCancellationinNET4.pdf)。
已更新,这是可验证的here in the Reference Source。
同样重要的是要注意,取消回调是在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
的范围非常重要。
答案 1 :(得分:3)
为什么它甚至实现了IDisposable?它必须释放什么资源?
IDisposable
通常用于与释放资源完全无关的事情;它是一种方便的方法,可确保在using
块结束时完成某些操作,即使发生异常也是如此。有些人(不是我)认为这样做是对Dispose
模式的滥用。
在CancellationToken.Register
的情况下,“某事”是取消注册回调。
请注意,在您在问题中发布的代码中,在 < em>(编辑:自问题编辑以来不再正确) using
上使用CancellationTokenRegistration
块几乎肯定是一个错误:因为wc.DownloadStringAsync
立即返回,在操作有可能被取消之前,回调将立即取消注册,因此即使wc.CancelAsync
已发出信号,也不会调用CancellationToken
。如果等待wc.DownloadStringAsync
的调用是有意义的,因为在这种情况下,在using
完成之前不会到达wc.DownloadStringAsync
块的结尾。
如果您不处理它会怎样?你泄漏了什么?
在这种情况下,会发生的是回调未注册。 它可能并不重要,因为它仅由取消令牌引用,并且由于 编辑:实际上,这是不正确的;请参阅Noseratio的解释答案 CancellationToken
是一种通常仅存储在堆栈中的值类型,因此当引用超出范围时引用将消失。因此在大多数情况下它不会泄漏任何东西,但如果你将CancellationToken
存储在堆上的某个地方,它可能就会泄漏。