7

如果我调用Dispose()锁定的对象会发生什么?

lock (obj)
{
    obj.Dispose();
}

如果我Monitor.Exit()在这种情况下跳过通话会发生什么:

Monitor.Enter(obj);
obj.Dispose();
4

5 回答 5

6

Dispose 的实现通常不是线程安全的。

这是因为您不应该在仍有可能使用的引用时处置对象。这意味着您不应该处置任何其他线程持有引用的对象。

IDisposable/Dispose不是一种管理对象生命周期的方法,无论是一般的还是跨线程的——它是一旦对象生命周期结束就释放对象资源的范例。(该using语句是一个习惯用法/范式,用于对象的正确生命周期是语句的范围。)

因此,如果您在另一个线程锁定对象的情况下调用 dispose,则已经出现了非常严重的问题。

于 2013-09-30T12:28:27.160 回答
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.EnterMonitor.Exit这样您就不必担心了。

于 2013-09-30T14:51:44.913 回答
4

范式与Dispose范式不同lock。您可以安全地处置当前持有互斥锁的对象;但是,您仍然应该在之后释放锁。如果您不这样做,则调用Monitor.Enter(现已处置的)对象的其他线程将无限期地阻塞。

于 2013-09-30T12:22:28.853 回答
1

对于您的第一个问题 - 发生的情况是在任何给定时刻只有一个威胁可以调用 Dispose()。对于您的第二个问题 - 如果多个线程运行此代码,其中第一个将调用 Dispose 方法。其余的将永远阻塞。

dispose 方法没有什么特别之处。它就像任何其他方法一样,只是它参与了一些语法糖(例如,使用语句)。

于 2013-09-30T12:23:56.560 回答
1

您始终可以锁定现有对象,该Dispose方法不会删除您的对象,因此对象的引用仍然存在。该Dispose()方法不影响对象的实例。

于 2013-09-30T12:34:52.057 回答