任务和垃圾收集有什么问题?
在使用从任务并行库派生的API和类时,开发人员何时需要关注垃圾收集的影响?
.NET Task实例在运行期间是否超出范围? ,似乎给人一种安全感,你不必担心将任务保持在范围内。 然而,问题似乎仅限于在ThreadPool上运行的任务,然后它们由ThreadPool rooted
。 但是,如果我正确理解这篇MSDN博客文章 ,那么SO问题的建议通常不适用,因为TaskCompletionSource
中的任务不是类似的rooted
。
是否只关注TaskCompletionSource
的直接使用时间?
但是,在使用API时,您不知道任务的来源。 如果提供的Task
来自TaskCompletionSource
或其他一些非根源,您是否需要担心存储对continuation的引用?
由于需要考虑任务是否已植根(Async I / O Tasks是否为root?),这似乎很快变得不方便和复杂。 我很难找到关于主题的信息,但它是一个很受欢迎的库,我觉得我不应该阅读反编译的源代码来确定我是否需要担心垃圾收集器的竞争条件,所以我想我必须是遗失或误解的东西。
当你有未完成的TaskCompletionSource
,总会有两个选项:
-
将来有些东西可能会完成TCS。 这意味着某些东西含有对TCS的引用,这意味着它无法获得GCed。
正常规则仍然适用于那个东西,所以你可能需要担心保持这种根源。
-
什么都不会完成那个TCS。 这意味着TCS及其任务很快就会得到GC,但是没有工作没有风险(因为没有工作)。
唯一的问题是当Task
由TaskCompletionSource
提供时,任何应该利用TaskCompletionSource来设置结果都符合垃圾收集的条件。 不幸的是,在这种情况下,API的消费者无法做任何事情,除非他们可以访问并且可以保存对任何内容的引用。 因此,对于API实现者的提供者来说,这也是在返回这样的任务时需要注意这一点的问题。
由于缺乏更好的资源,我必须通过组合测试(试错)和阅读源代码来确定以上内容。 但是,在没有文档的情况下,这些可能是实现细节,并且可能会在.NET Framework的未来版本中进行更改。
进一步解释
Task
类是密封的,看起来TaskCompletionSource
使用非公共API工作。 因此,排除可能使用非公共API的其他MS API并假设库不反映使用Task
的内部,唯一关注的实现是Task
和TaskCompletionSource
。
任务(不是来自TaskCompletionSource)
除了由TaskCompletionSource
创建的TaskCompletionSource
,还使用Task
或TaskFactory
上的成员创建Task
。 由这些方法之一创建的任何已启动 Task
都绑定到TaskScheduler
。 由于根据基于任务的异步模式指南 ( 摘录 )应该启动任何返回,因此非启动不是消费者需要担心的情况。
根据MSDN上的TaskScheduler.QueueTask
文档(强调我的):
典型的实现将任务存储在内部数据结构中 ,该内部数据结构将由将在未来某个时间执行这些任务的线程提供服务。
因此,只要使用的TaskScheduler
实现遵循该规则,调度程序就会导致对Task维护一个引用。 只要调度程序使用的数据结构处于活动状态,这应该使Task保持活动状态。
构建到框架中的两个TaskScheduler
实现在排队任务的存储方面应该是安全的。 一个是单例,另一个是SynchronizationContext
支持,因此只要上下文存在,排队的任务就会生根。
TaskScheduler
的基础构造函数将所有创建的实例TaskScheduler实例注册到活动实现的静态列表中,这样可以防止任何自定义实现在其他方式可能有资格进行收集时进行垃圾回收。 除非TaskScheduler
在排队任务中做了一些粗鲁的事情,否则不会出现与自定义TaskScheduler
的范围相关的问题。
总的来说,这里没有什么可担心的。
TaskCompletionSource
TaskCompletionSources不保证可以被任何东西植根。 [1]因此,在设置结果之前,TaskCompletionSource确实存在垃圾收集的可能性。
如果用于确保完成TaskCompletionSource的相关对象是对象的成员,则维护对调用Task-returning方法的对象的引用可能会有所不同。 虽然我找不到TAP / TPL的任何指导原则,但应该避免这种情况,我希望它们在发生时能够清楚地记录下来。
TaskCompletionSource返回的Task不维护对原始TaskCompletionSource的引用,更不用说引用TaskCompletionSource来设置结果的其他内容了。 因此,使用者是否维护对返回的Task的引用不会影响此问题。
在完成所需的对象仅限于任务返回方法的情况下,API使用者实际上无法确保正确性,并且这种情况应被视为提供API中的错误。