鉴于以下内容:
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);
考虑到多线程和垃圾收集模式,在什么情况下会导致WaitForPendingFinalizers
出现死锁?
注意:我不是在寻找您不应该致电GC.Collect
的原因的答案。
答案 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.Collect
和GC.WaitForPendingFinalizers
时,您不会遇到任何类型的死锁情况,除非您正在Finalize
方法中访问托管对象。使用公共范围调用其他对象的方法可能可能导致不可预测的结果,包括死锁。原因是您无法完全控制其他对象的锁定模式。当你的终结者方法试图访问它时,任何人都可以锁定它。
此外,正如LukeH的回答所示,在终结器中显式锁定this
几乎保证导致死锁。您可以阅读Jeffrey Richter关于该here的原始文章。
一般情况下,您应该只在终结器中释放非托管资源,这样可以减轻对死锁的任何担忧。