在调用GC.Collect和GC.WaitForPendingFinalizers时,是否会发生死锁?

11

考虑以下情况:

GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);

考虑到多线程和垃圾回收模式,什么情况下会在WaitForPendingFinalizers上出现死锁?

注意:我不希望得到有关为什么不应调用GC.Collect的答案。


2
这是一个严格的抢占式问题,还是你遇到了实际的死锁情况?(如果你正在进行故障排除,将你的终结器方法的代码发布出来会很有帮助。) - Cody Gray
3个回答

6
// causes a deadlock when built with release config and no debugger attached
// building in debug mode and/or attaching the debugger might keep
// badIdea alive for longer, in which case you won't see the deadlock
// unless you explicitly set badIdea to null after calling Monitor.Enter

var badIdea = new BadIdea();
Monitor.Enter(badIdea);

GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);

// ...

public class BadIdea
{
    ~BadIdea()
    {
        lock (this)
        {
            // ...
        }
    }
}

5
GC.CollectGC.WaitForPendingFinalizers的调用不会导致死锁,除非您在Finalize方法中访问托管对象。调用其他公共作用域对象的方法可能会导致不可预测的结果,包括死锁。原因是您无法完全控制这些其他对象的锁定模式。当您的终结器方法尝试访问它时,任何人都可以锁定它。

此外,在终结器中明确锁定this几乎肯定会导致死锁,正如LukeH的答案所示。您可以在此处阅读Jeffrey Richter的原始文章。

通常,您应该仅在终结器中释放非托管资源,这应该消除有关死锁的任何顾虑。


实际上,有一些情况下,让可终结对象持有对其他托管对象的引用可能是有用的。例如,由于Bitmap对象很昂贵,因此需要存储数百个16x16图标的程序可能会定义一个类,该类分配16x1024像素位图,每个位图可以容纳64个图标,然后返回每个对象,其中包含对位图分配器的引用以及它所“拥有”的部分的指示。当这样一个“位图块”对象被处理时,分配器可以使较大位图的该部分可供其他人使用。 - supercat
即使只有“位图块”对象内部对较大位图的强引用,因此放弃所有这些对象将使较大的位图有资格进行终结,但放弃与位图相关联的除一个“位图块”对象之外的所有对象将使整个位图分配但大多不可用于其他目的。向“位图块”对象添加终结器将使主对象能够重用被放弃的块。当然,这样的代码必须小心编写,以避免死锁或其他线程问题,但它仍然是有用的。 - supercat
我不明白那与这个答案或问题有什么关系。@supercat - Cody Gray
我是在回应你的最后一句话,但是重新阅读后我注意到它说“通常情况下……”我的观点是,在某些情况下,让终结器与托管资源交互是合适的。 - supercat

4

Jeffrey Richter提到了一个著名的关于WaitForPendingFinalizers的死锁问题。具体情况可以在这里查看:http://haacked.com/archive/2005/04/12/neverlockthis.aspx

class MyClass
{

private bool isDisposed = false;
~MyClass() { lock(this) { if(!isDisposed) { // ... } } } } ... MyClass instance = new MyClass();
Monitor.Enter(instance); instance = null;
GC.Collect(); GC.WaitForPendingFinalizers();

这是因为错误地使用了lock(this)导致的。无论如何,这种情况下WaitForPendingFinalizers被锁定了。


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