任务和垃圾回收存在哪些需要注意的问题?

14
当使用从Task Parallel Library派生的API和类时,开发人员何时需要关注垃圾回收的影响?虽然Can .NET Task instances go out of scope during run?给人一种安全感,认为不需要担心任务是否在作用域内,但是该问题似乎仅限于在线程池上运行的任务,在那里它们由线程池rooted。但是,如果我正确理解此MSDN博客文章,则来自TaskCompletionSource的任务不是同样被rooted的,因此这个问题的建议并不普遍适用。
直接使用TaskCompletionSource是唯一需要关注的时间吗?然而,在使用API时,您不知道Task来自何处。那么,如果提供的Task来自非rooted源(如TaskCompletionSource),您是否需要担心存储延续的引用?
考虑到任务是否rooted(异步I/O任务是否rooted?)似乎很快变得不方便和复杂。我很难找到有关此主题的信息,但这是一个足够受欢迎的库,我觉得我不应该阅读反编译的源代码来确定是否需要担心与垃圾回收器发生竞争条件,因此我认为我必须遗漏或误解了某些东西。

不,你不必担心那个。在我看来,你问错了问题,应该问:“当我自己没有保留引用时,一个任务对象如何保持活动状态?” - Hans Passant
1
你是否担心你的任务对象在完成之前会不知何故地超出范围并被终止?或者你是否担心你的任务对象会永远挂起并不必要地消耗内存? - Jim Mischel
@JimMischel 我更担心未在范围内的“工作”没有被执行。 - vossad01
@HansPassant 这里的适当方法是什么呢?编辑这个问题?关闭它并单独提出你所陈述的问题? - vossad01
正确的方法是运行内存分析器并记录您的问题。然后提出一个关于它的问题,并用录音记录下来。我的预测是你不会问这个问题。 - Hans Passant
2个回答

8

当您有未完成的TaskCompletionSource时,通常会有两个选项:

  1. 未来可能会有某些事情完成TCS。这意味着该事物持有对TCS的引用,因此它不能被GC回收。

    该事物仍然遵循普通规则,因此您可能需要担心保持其根源。

  2. 永远不会完成该TCS。这意味着TCS及其任务很快可能会被GC回收,但没有工作风险(因为没有工作)。


那个保存TCS并将在某个幸运的日子完成它的“某物”,也可能被GC回收。因此,同样的预防措施也适用,对吧? - Vlad

7
唯一需要关注的是,当TaskTaskCompletionSource提供时,任何应该利用TaskCompletionSource设置结果的内容都有资格进行垃圾回收。不幸的是,在这种情况下,API的使用者无法做任何事情,除非他们可以访问并持有对其的引用。因此,这也是API提供者在返回此类任务时需要注意的问题。
缺乏更好的资源,我只能通过测试(试错)和阅读源代码来确定上述内容。然而,在没有文档的情况下,这些可能是实现细节,并且可能会在未来的.NET Framework版本中发生变化。
进一步解释 Task类是密封的,而且似乎TaskCompletionSource使用了一个非公共的API。因此,除了其他可能使用非公共API的MS API之外,并且假设库没有反射地使用Task的内部,唯一需要关注的实现是TaskTaskCompletionSource
除了由TaskCompletionSource创建的任务,还可以使用TaskTaskFactory的成员创建任务。使用这些方法之一创建的任何已启动的Task都绑定到一个TaskScheduler。根据基于任务的异步模式指南(摘录),任何返回的任务都应该是已经启动的,未启动的不是消费者需要担心的情况。
根据MSDN上TaskScheduler.QueueTask的文档(我强调部分):
“典型的实现方式是将任务存储在内部数据结构中,该数据结构将由线程服务,这些线程将在将来的某个时间执行这些任务。”
因此,只要所使用的 TaskScheduler 实现遵循该规则,调度程序就会导致对 Task 的引用被保留。这应该使 Task 在调度程序使用的数据结构存活期间保持存活。
内置于框架中的两个 TaskScheduler 实现在排队任务的存储方面应该是安全的。一个是单例,另一个由 SynchronizationContext 支持,因此只要上下文存在,排队的任务就会被根据。 TaskScheduler 的基本构造函数会在静态列表中注册所有已创建的 TaskScheduler 实例,这应该防止任何自定义实现在其本来可以进行垃圾回收时被回收。除非 TaskScheduler 在排队任务时做了一些不端的事情,否则不应出现与自定义 TaskScheduler 范围相关的任何问题。
总的来说,这里真的没有什么可担心的。

TaskCompletionSource

TaskCompletionSources不能保证被任何东西所引用。[1] 因此,存在TaskCompletionSource在设置结果之前被垃圾回收的潜在可能性。

如果调用返回Task的方法的对象中包含确保TaskCompletionSource完成所需的相关对象,则保留对该对象的引用可能会有所不同。虽然我找不到任何TAP/TPL指南表明应避免这种情况,但希望当出现这种情况时能清楚地记录下来。

由TaskCompletionSource返回的Task不会保留对原始TaskCompletionSource的引用,更别提其他应该引用TaskCompletionSource以设置结果的内容了。因此,使用者是否保留对返回的Task的引用都不会影响此问题。

如果完成所需的对象仅限于返回任务的方法,则API使用者无法确保正确性,在这种情况下,应将其视为提供API的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接