类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的正确和推荐策略是什么?
我已经很久没有问过这个问题了,得到了很多有用的答案,但我遇到了一个与此相关的有趣问题,我想把它作为另一个答案张贴在这里:
只有在确定没有人试图获取CTS的Token属性时,才应该调用CancellationTokenSource.Dispose()。否则不应该调用Dispose(),因为它会创建一个竞态条件。例如,看这里:
https://github.com/aspnet/AspNetKatana/issues/108
在修复此问题时,之前执行cts.Cancel()的代码;cts.Dispose ();被编辑为只执行cts.Cancel();因为任何不幸的人在Dispose被调用后试图获得取消令牌以观察其取消状态,将不幸地还需要处理ObjectDisposedException——除了他们计划处理的OperationCanceledException之外。
与此修复相关的另一个关键观察是由Tratcher提出的:“只需要对不会被取消的令牌进行处理,因为取消执行所有相同的清理工作。”
也就是说,只做Cancel()而不是dispose就足够了!
您应该始终处置CancellationTokenSource。
如何处置它完全取决于场景。你提出了几个不同的场景。
using only works when you're using CancellationTokenSource on some parallel work that you're waiting. If that's your senario, then great, it's the easiest method.
When using tasks, use a ContinueWith task as you indicated to dispose of CancellationTokenSource.
For plinq you can use using since you're running it in parallel but waiting on all of the parallel running workers to finish.
For UI, you can create a new CancellationTokenSource for each cancellable operation that is not tied to a single cancel trigger. Maintain a List<IDisposable> and add each source to the list, disposing all of them when your component is disposed.
For threads, create a new thread that joins all the worker threads and closes the single source when all of the worker threads finished. See CancellationTokenSource, When to dispose?
总有办法的。IDisposable实例应该总是被释放。示例通常不会,因为它们要么是显示核心用法的快速示例,要么是因为添加所演示的类的所有方面对于示例来说过于复杂。示例只是一个示例,不一定(甚至通常)是产品质量代码。并不是所有示例都可以原样复制到生产代码中。
这个答案仍然出现在谷歌的搜索中,我相信投票选出的答案并没有给出完整的故事。在查看了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文档中所描述的那样,结果是未定义的。