我可以丢弃锁定的物体吗?

7 c# multithreading

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

lock (obj)
{
    obj.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

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

Monitor.Enter(obj);
obj.Dispose();
Run Code Online (Sandbox Code Playgroud)

Ben*_*Ben 6

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

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

IDisposable/ Dispose不是一般管理或跨线程管理对象生存期的方法- 它是一旦生命周期结束后释放对象资源的范例.(该using语句是一个习惯用法/范例,用于对象的适当生命周期是语句的范围.)

因此,如果您在另一个线程锁定了对象时调用dispose,则某些内容已经非常错误.


Bri*_*eon 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你编译并运行它,你会发现打印出"~Foo".这表明Monitor.Enter内部没有参考.因此,虽然不建议跳过Monitor.Exit调用,但您可以放心地知道它不会导致内存泄漏.1


1 实际上,这可能不完全正确.虽然很容易证明没有托管内存泄漏,但在非托管领域可能会有所不同.如果不看SSCLI代码,我们真的不知道Monitor.Enter内部在做什么.也许它在非托管堆或堆栈中分配一个额外的数组槽(或其他).我想微软认为这个模糊的场景,但谁知道呢.问题的关键是,Monitor.EnterMonitor.Exit调用应始终配对,这样你就不必担心.