你可以在调用GC.Collect和GC.WaitForPendingFinalizers时遇到死锁吗?

时间:2010-12-08 10:47:48

标签: c# .net garbage-collection

鉴于以下内容:

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

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

注意:我不是在寻找您不应该致电GC.Collect的原因的答案。

3 个答案:

答案 0 :(得分:5)

// 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)
        {
            // ...
        }
    }
}

答案 1 :(得分:2)

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();

这是由于错误使用锁(this)引起的。无论如何,这是WaitForPendingFinalizers被锁定的情况。

答案 2 :(得分:2)

在调用GC.CollectGC.WaitForPendingFinalizers 时,您不会遇到任何类型的死锁情况,除非您正在Finalize方法中访问托管对象。使用公共范围调用其他对象的方法可能可能导致不可预测的结果,包括死锁。原因是您无法完全控制其他对象的锁定模式。当你的终结者方法试图访问它时,任何人都可以锁定它。

此外,正如LukeH的回答所示,在终结器中显式锁定this几乎保证导致死锁。您可以阅读Jeffrey Richter关于该here的原始文章。

一般情况下,您应该只在终结器中释放非托管资源,这样可以减轻对死锁的任何担忧。