为何释放CancellationTokenSource会同步释放其关联的Linked CTS资源?
为什么释放作业专属CancellationTokenSource会连带回收关联的Linked CancellationTokenSource?
问题背景
在.NET 4.7.2控制台应用排查内存泄漏时,通过三组测试发现:
- 仅释放关联的
linkedCts:仅能回收一半对象 - 同时释放
linkedCts与作业专属Cts:所有非全局CancellationTokenSource都被回收 - 仅释放作业专属
Cts:效果与同时释放两者一致,所有非全局对象都被回收
即使linkedCts关联着存活的全局CancellationToken,该现象依然存在。
核心原因:LinkedCts的内部依赖与生命周期关联
要理解这个现象,得从CancellationTokenSource.CreateLinkedTokenSource的内部实现逻辑说起:
LinkedCts的依赖关系
当你创建linkedCts时,它会内部注册回调,监听所有传入的CancellationToken(包括全局ct和作业Cts的token)的取消事件。这里的关键是:linkedCts会持有对作业Cts的token的强引用,以便监听其状态变化- 作业Cts不会持有
linkedCts的引用
释放作业Cts时的连锁反应
当你释放作业Cts时,它会完成内部的取消信号,标记自身为已完成状态。此时:linkedCts监听的作业Cts token触发取消事件,linkedCts会自动进入取消状态linkedCts内部会清理对作业Cts token的引用,同时自身的资源(如等待句柄、回调注册)会被释放- 虽然
linkedCts还关联着全局ct,但全局ct的CancellationTokenSource并不会持有linkedCts的引用——linkedCts只是注册了全局ct的回调,而这种注册不会形成反向的强引用链。只要你的代码中没有其他地方持有linkedCts的引用,GC就可以回收它。
仅释放linkedCts时的局限性
释放linkedCts只会清理它自身的资源,但作业Cts并没有被触发释放或取消,它仍然处于存活状态(只要代码中还有对它的引用,或者未完成其生命周期)。因此作业Cts无法被GC回收,也就出现了“仅能回收一半对象”的结果。
关键结论
linkedCts的生命周期本质上依赖于所有关联的token的状态:当其中一个关联的token(这里是作业Cts的token)被释放/取消,linkedCts会自动完成自身的生命周期,不再被其他引用链锁定,最终被GC回收。而反过来,仅释放linkedCts不会影响作业Cts的生命周期,因此无法回收作业Cts对象。
内容的提问来源于stack exchange,提问作者Maddin
相关产品推荐
相关产品推荐

