如果我在锁定的对象上调用Dispose()
会怎样?
lock (obj)
{
obj.Dispose();
}
如果我在这种情况下跳过Monitor.Exit()
电话会怎样?
Monitor.Enter(obj);
obj.Dispose();
答案 0 :(得分:6)
Dispose的实现通常不是线程安全的。
这是因为在仍有可能使用的引用时,不应丢弃对象。这意味着你不应该处理任何其他线程持有引用的对象。
IDisposable
/ Dispose
不是管理对象生命周期的方法,无论是一般还是跨线程 - 它是释放对象资源的范例它的生命周期结束了。 (using
语句是一个习惯用法/范例,用于对象的适当生命周期是语句的范围。)
因此,如果您在另一个线程已锁定对象时调用dispose,则某些内容已经非常错误。
答案 1 :(得分:5)
如果我在锁定对象上调用Dispose()会怎样?
首先,对象本身并未锁定(受保护)。 lock
关键字中使用的引用用于标记或标记代码的一部分,该部分不应与使用相同对象引用的任何其他(或相同)代码段同时运行。它实际上并不影响对象本身。这是关于锁如何在.NET中工作的一种非常常见的误解。
在此上下文中,调用Dispose
与调用任何其他方法没有什么不同。没有什么特别的事情发生。它只是意味着两个不同的线程不能同时执行Dispose
。你所做的不仅是可以接受的,而且如果这个类不是线程安全的,那么实际上是推荐的。
如果我在这种情况下跳过Monitor.Exit()调用会发生什么:
你应该总是释放锁。请记住,锁不会对对象引用本身起作用,因此应该更容易理解您将在获取状态中保持锁定状态。物体被丢弃并不重要。请记住,Monitor
(或lock
)不会锁定或保护对象本身。它只标记或标记一段代码。如果某个线程再次尝试获取具有相同对象的锁,则该线程将被强制无限期地等待,从而可能导致死锁。
一个更有趣的问题是,这是否会导致内存泄漏。 Monitor.Enter
根对象吗?答案是不。这可以通过以下示例进行演示。
public class Program
{
public static void Main(string[] args)
{
var foo = new Foo();
Monitor.Enter(foo);
foo = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
}
internal class Foo
{
~Foo()
{
Console.WriteLine("~Foo");
}
}
如果你编译并运行它,你会发现打印出“~Foo”。这表明Monitor.Enter
内部没有引用。因此,虽然不建议跳过Monitor.Exit
调用,但在知道它不会导致内存泄漏时,您可以放心。 1
1 实际上,这可能不完全正确。虽然很容易证明没有托管内存泄漏,但在非托管领域可能会有所不同。如果没有查看SSCLI代码,我们真的不知道Monitor.Enter
在内部做了什么。也许它在非托管堆或堆栈中分配一个额外的数组槽(或其他)。我想微软认为这个模糊的场景,但谁知道呢。关键是Monitor.Enter
和Monitor.Exit
来电应始终配对,这样您就不用担心了。
答案 2 :(得分:4)
Dispose
范例与lock
范例不同。您可以安全地处置当前持有互斥锁的对象;但是,你之后仍然应该释放锁。如果你没有这样做,那么在(现在处置的)对象上调用Monitor.Enter
的其他线程将无限期地阻塞。
答案 3 :(得分:1)
对于你的第一个问题 - 发生的事情是在任何给定时刻只有一个威胁可能会调用Dispose()。 对于第二个问题 - 如果多个线程运行此代码,则第一个问题将调用Dispose方法。其余的将永远阻止。
dispose方法中有 nothing 特殊。它就像任何其他方法一样,除了它参与一些语法糖(例如,使用语句)。
答案 4 :(得分:1)
您始终可以锁定现有对象,Dispose
方法无法删除您的对象,因此该对象的引用仍然存在。 Dispose()
方法不会影响对象的实例。