类CancellationTokenSource是可丢弃的。在Reflector中快速查看可以证明使用了KernelEvent,这是一个(很可能)非托管资源。
由于CancellationTokenSource没有终结器,如果我们不释放它,GC就不会这样做。
另一方面,如果查看MSDN文章“托管线程中的取消”中列出的示例,就会发现只有一个代码片段处理了令牌。
在代码中处理它的正确方法是什么?
You cannot wrap code starting your parallel task with using if you do not wait for it. And it makes sense to have cancellation only if you do not wait.
Of course you can add ContinueWith on task with a Dispose call, but is that the way to go?
What about cancelable PLINQ queries, which do not synchronize back, but just do something at the end? Let's say .ForAll(x => Console.Write(x))?
Is it reusable? Can the same token be used for several calls and then dispose it together with the host component, let's say UI control?
因为它没有一个Reset方法来清理IsCancelRequested和Token字段,我认为它是不可重用的,因此每次你开始一个任务(或PLINQ查询),你应该创建一个新的。这是真的吗?如果是,我的问题是,在那些许多CancellationTokenSource实例上处理Dispose的正确和推荐策略是什么?
我认为目前的答案没有一个令人满意。经过研究,我发现了Stephen Toub的回复(参考):
It depends.
In .NET 4, CTS.Dispose served two primary purposes. If the
CancellationToken's WaitHandle had been accessed (thus lazily
allocating it), Dispose will dispose of that handle. Additionally, if
the CTS was created via the CreateLinkedTokenSource method, Dispose
will unlink the CTS from the tokens it was linked to. In .NET 4.5,
Dispose has an additional purpose, which is if the CTS uses a Timer
under the covers (e.g. CancelAfter was called), the Timer will be
Disposed.
It's very rare for CancellationToken.WaitHandle to be used,
so cleaning up after it typically isn't a great reason to use Dispose.
If, however, you're creating your CTS with CreateLinkedTokenSource, or
if you're using the CTS' timer functionality, it can be more impactful
to use Dispose.
我认为大胆的部分是重要的部分。他使用了“更有影响力”,这让它有点模糊。我将其解释为在这些情况下应该调用Dispose,否则就不需要使用Dispose。
我已经很久没有问过这个问题了,得到了很多有用的答案,但我遇到了一个与此相关的有趣问题,我想把它作为另一个答案张贴在这里:
只有在确定没有人试图获取CTS的Token属性时,才应该调用CancellationTokenSource.Dispose()。否则不应该调用Dispose(),因为它会创建一个竞态条件。例如,看这里:
https://github.com/aspnet/AspNetKatana/issues/108
在修复此问题时,之前执行cts.Cancel()的代码;cts.Dispose ();被编辑为只执行cts.Cancel();因为任何不幸的人在Dispose被调用后试图获得取消令牌以观察其取消状态,将不幸地还需要处理ObjectDisposedException——除了他们计划处理的OperationCanceledException之外。
与此修复相关的另一个关键观察是由Tratcher提出的:“只需要对不会被取消的令牌进行处理,因为取消执行所有相同的清理工作。”
也就是说,只做Cancel()而不是dispose就足够了!
我认为目前的答案没有一个令人满意。经过研究,我发现了Stephen Toub的回复(参考):
It depends.
In .NET 4, CTS.Dispose served two primary purposes. If the
CancellationToken's WaitHandle had been accessed (thus lazily
allocating it), Dispose will dispose of that handle. Additionally, if
the CTS was created via the CreateLinkedTokenSource method, Dispose
will unlink the CTS from the tokens it was linked to. In .NET 4.5,
Dispose has an additional purpose, which is if the CTS uses a Timer
under the covers (e.g. CancelAfter was called), the Timer will be
Disposed.
It's very rare for CancellationToken.WaitHandle to be used,
so cleaning up after it typically isn't a great reason to use Dispose.
If, however, you're creating your CTS with CreateLinkedTokenSource, or
if you're using the CTS' timer functionality, it can be more impactful
to use Dispose.
我认为大胆的部分是重要的部分。他使用了“更有影响力”,这让它有点模糊。我将其解释为在这些情况下应该调用Dispose,否则就不需要使用Dispose。
这个答案仍然出现在谷歌的搜索中,我相信投票选出的答案并没有给出完整的故事。在查看了CancellationTokenSource (CTS)和CancellationToken (CT)的源代码后,我相信对于大多数用例,以下代码序列是好的:
if (cancelTokenSource != null)
{
cancelTokenSource.Cancel();
cancelTokenSource.Dispose();
cancelTokenSource = null;
}
上面提到的m_kernelHandle内部字段是CTS和CT类中支持WaitHandle属性的同步对象。只有当您访问该属性时,它才被实例化。因此,除非你在任务中使用WaitHandle进行一些老式的线程同步,否则调用dispose将没有任何效果。
当然,如果你正在使用它,你应该按照上面其他答案的建议去做,并延迟调用Dispose,直到使用该句柄的任何WaitHandle操作完成,因为,正如在WaitHandle的Windows API文档中所描述的那样,结果是未定义的。