Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么存在CancellationTokenRegistration以及它为什么实现IDisposable_C#_.net_Task Parallel Library_Idisposable_Cancellation Token - Fatal编程技术网

C# 为什么存在CancellationTokenRegistration以及它为什么实现IDisposable

C# 为什么存在CancellationTokenRegistration以及它为什么实现IDisposable,c#,.net,task-parallel-library,idisposable,cancellation-token,C#,.net,Task Parallel Library,Idisposable,Cancellation Token,我一直看到使用取消的代码。在CancellationTokenRegistration结果上使用using子句注册: using (CancellationTokenRegistration ctr = token.Register(() => wc.CancelAsync())) { await wc.DownloadStringAsync(new Uri("http://www.hamster.com")); } 我明白了,您应该确保Dispose一个IDisposable,

我一直看到使用
取消的代码。在
CancellationTokenRegistration
结果上使用
using
子句注册

using (CancellationTokenRegistration ctr = token.Register(() => wc.CancelAsync()))
{
    await wc.DownloadStringAsync(new Uri("http://www.hamster.com"));
}
我明白了,您应该确保
Dispose
一个
IDisposable
,但为什么它甚至实现了
IDisposable
?它必须释放哪些资源?唯一的方法就是平等


如果不
处理它会发生什么?您泄漏了什么?

此模式是确保自动调用
CancellationTokenRegistration.Unregister()
的方便方法。Stephen Toub经常在他的博客文章中使用它,例如

我明白你应该确保你处理了一个IDisposable,但为什么 它甚至实现了IDisposable吗?它需要什么资源 释放?唯一的方法就是平等

在我看来,最好的答案可以在微软的Mike Liddell的帖子中找到:

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

当回调注册到
CancellationToken
时,返回 对象是一个
CancellationTokenRegistration
。这是一种轻型结构类型 即
IDiposable
,处理此注册对象会导致 要取消注册的回调。保证在
Dispose()
方法已返回,注册的回调既不是 运行nor将随后开始。其结果是
CancellationTokenRegistration.Dispose()
必须阻止回调 目前正在执行。因此,所有注册的回调都应该是快速的 并且在任何显著的持续时间内都不会阻塞

Mike Liddell的另一份相关文件是

已更新,这是可验证的

还需要注意的是,取消回调是用
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.Dispose()
显式地使用try/finally)将一次性的CancellationTokenRegistration范围限定为)

为什么它甚至实现了IDisposable?它必须释放哪些资源

IDisposable
通常用于与释放资源完全无关的事情;这是一种方便的方法,可以确保在
结束时使用
块执行某些操作,即使发生异常也是如此。有些人(不是我)认为这样做是对
Dispose
模式的滥用

CancellationToken.Register
的情况下,“something”是回调的注销

请注意,在您在问题中发布的代码中,在
CancellationTokenRegistration
上使用
using
块几乎肯定是一个错误:因为
wc.DownloadStringAsync
会立即返回,回调会在有机会取消操作之前立即取消注册,因此,
wc.CancelAsync
将永远不会被调用,即使发出了
CancellationToken
信号。如果等待调用
wc.DownloadStringAsync
,这是有意义的,因为在这种情况下,在
wc.DownloadStringAsync
完成之前,不会到达
using
块的末尾。(编辑:问题编辑后不再为真)

如果你不处理它会怎么样?你漏了什么


在本例中,发生的情况是回调没有被取消注册。这可能并不重要,因为它只被取消令牌引用,而且由于
CancellationToken
是一种通常只存储在堆栈上的值类型,因此引用在超出范围时将消失。因此,在大多数情况下,它不会泄漏任何信息,但如果将
CancellationToken
存储在堆的某个位置,它可能会泄漏。编辑:事实上,这是不正确的;请参阅Noseratio的答案以了解解释

它是dispose滥用,它应该有一个Unregister()方法。@HansPassant:它是否为“滥用”取决于人们是否将
IDisposable.dispose看作是为了清理资源而存在的,或者是为了确保需要完成的事情,完成(资源清理是“必要操作”的主要类型,但不是唯一类型)。@HansPassant-它在asp.net MVC中也大量使用。如果框架作者自己做,我不认为这是滥用。ExecutionContext不也会被捕获到一个“常规”lambda表达式中吗?这不需要处理…@l3arnon,不,不会的。只有当涉及潜在的线程切换时,框架才会捕获
ExecutionContext
。如果为任何“常规”lambda回调捕获/恢复它,将是相当大的开销。更多。关于更新,你确定这是真的吗?注册链接到