如果我调用Dispose()
锁定对象会发生什么?
lock (obj)
{
obj.Dispose();
}
Run Code Online (Sandbox Code Playgroud)
如果我Monitor.Exit()
在这种情况下跳过呼叫会发生什么:
Monitor.Enter(obj);
obj.Dispose();
Run Code Online (Sandbox Code Playgroud)
Dispose的实现通常不是线程安全的.
这是因为在仍有可能使用的引用时,不应丢弃对象.这意味着您不应该处置任何其他线程持有引用的对象.
IDisposable
/ Dispose
不是一般管理或跨线程管理对象生存期的方法- 它是一旦生命周期结束后释放对象资源的范例.(该using
语句是一个习惯用法/范例,用于对象的适当生命周期是语句的范围.)
因此,如果您在另一个线程锁定了对象时调用dispose,则某些内容已经非常错误.
如果我在锁定对象上调用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");
}
}
Run Code Online (Sandbox Code Playgroud)
如果你编译并运行它,你会发现打印出"~Foo".这表明Monitor.Enter
内部没有参考.因此,虽然不建议跳过Monitor.Exit
调用,但您可以放心地知道它不会导致内存泄漏.1
1 实际上,这可能不完全正确.虽然很容易证明没有托管内存泄漏,但在非托管领域可能会有所不同.如果不看SSCLI代码,我们真的不知道Monitor.Enter
内部在做什么.也许它在非托管堆或堆栈中分配一个额外的数组槽(或其他).我想微软认为这个模糊的场景,但谁知道呢.问题的关键是,Monitor.Enter
并Monitor.Exit
调用应始终配对,这样你就不必担心.