类初始化完成后将Task引用替换为Task.CompletedTask对GC有何影响?
问题
我有一个仅需在生命周期内完成一次初始化的类,代码如下:
public class EmployeesAtPremisesAfterWork { private ConcurrentHashSet<int> isAtPremisesAfterWork = new ConcurrentHashSet<int>(); public Task WaitUntilInitialized; public EmployeesAtPremisesAfterWork(SomeInitializationHelper helper) { WaitUntilInitialized = Task.Run(async () => { var attachedValues = await helper.GetEmployeeAttachedValues(); isAtPremisesAfterWork.AddRange(attachedValues.AtPremisesAfterWork); WaitUntilInitialized = Task.CompletedTask; }); } }
我想了解两个问题:
- 初始化任务完成后,将
WaitUntilInitialized引用替换为Task.CompletedTask这一操作,对GC(垃圾回收)是否有影响? - 已完成的Task是否会保留对其上下文的引用?
回答
关于GC的影响:这个替换操作对GC是有利的。你通过
Task.Run创建的原始Task对象,在初始化完成后就没有实际作用了。把WaitUntilInitialized的引用切换到静态的Task.CompletedTask后,原始Task就失去了根引用(只要没有其他代码持有它的引用),GC就可以回收这个Task对象以及它关联的临时资源。而Task.CompletedTask是.NET内置的静态已完成任务实例,整个应用复用同一个对象,不会产生额外垃圾。关于上下文引用的保留:已完成的Task是否持有上下文引用,得看具体场景。你代码里的Task来自async匿名委托,这个委托会捕获构造函数中的
helper、当前类的实例(因为要访问类的字段)。在Task运行期间,它会持有这些引用;但当Task完成后,只要没有其他地方还引用这个Task,这些被捕获的引用就不会被Task继续持有——等原始Task被GC回收时,这些引用链就会断开,相关对象也能被正常回收。但如果有其他代码还拿着这个原始Task的引用,那它还是会保留上下文引用,阻止对应对象被GC回收。不过你替换成Task.CompletedTask后,原始Task没了引用,就能被GC回收,它带的上下文引用也会跟着释放。
内容的提问来源于stack exchange,提问作者Petr Vejchoda
相关产品推荐
相关产品推荐

