C# 我可以处理锁定的对象吗?

C# 我可以处理锁定的对象吗?,c#,multithreading,C#,Multithreading,如果对锁定的对象调用Dispose(),会发生什么 lock (obj) { obj.Dispose(); } 在这种情况下,如果跳过Monitor.Exit()调用会发生什么情况: Monitor.Enter(obj); obj.Dispose(); Dispose范例不同于lock范例。您可以安全地处置当前持有互斥锁的对象;但是,之后仍应释放锁。如果您未能执行此操作,则对(现在已处置)对象调用Monitor.Enter的其他线程将无限期阻止。对于您的第一个问题-发生的情况是,在任

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

lock (obj)
{
    obj.Dispose();
}
在这种情况下,如果跳过
Monitor.Exit()
调用会发生什么情况:

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

Dispose
范例不同于
lock
范例。您可以安全地处置当前持有互斥锁的对象;但是,之后仍应释放锁。如果您未能执行此操作,则对(现在已处置)对象调用
Monitor.Enter
的其他线程将无限期阻止。

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


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

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

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

IDisposable
/
Dispose
不是一种管理对象生存期的方法,无论是在一般情况下还是在线程之间-它是一种在对象生存期结束后释放其资源的范例。(当对象的正确生存期是语句的范围时,
using
语句是一种惯用用法/范例。)


因此,如果您在另一个线程锁定对象时调用dispose,则说明该对象已经出了很大问题。

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

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

lock (obj)
{
    obj.Dispose();
}
首先,对象本身一开始没有被锁定(保护)。
lock
关键字中使用的引用用于标记或标记不应与使用相同对象引用的任何其他(或相同)代码段同时运行的代码段。它实际上并不影响对象本身。这是一个关于锁在.NET中如何工作的常见误解

在此上下文中,调用
Dispose
与调用任何其他方法没有什么不同。没有什么特别的事情发生。这只意味着两个不同的线程不能同时执行
Dispose
。如果类不是线程安全的,那么您所做的不仅是可以接受的,而且实际上是推荐的

在这种情况下,如果跳过Monitor.Exit()调用会发生什么情况:

Monitor.Enter(obj);
obj.Dispose();
你应该总是释放锁。请记住,锁不会作用于对象引用本身,因此更容易理解的是,您将在获取状态下保留锁。处理对象并不重要。请记住,
监视器
(或
锁定
)不会锁定或保护对象本身。它只标记或标记一段代码。如果一个线程试图再次获取同一对象的锁,那么该线程将被迫无限期地等待,可能导致死锁

一个更有趣的问题是,这是否会导致内存泄漏。是否监视。输入对象的根目录?答案是否定的。这可以用下面的例子来说明

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
不会在内部保存引用。因此,虽然不建议跳过
监视器.Exit
呼叫,但您可以放心地知道它不会导致内存泄漏。1



1实际上,这可能并不完全正确。虽然很容易证明不存在托管内存泄漏,但非托管领域可能会有所不同。除了查看SSCLI代码之外,我们真的不知道
Monitor.Enter
在内部做什么。可能是在非托管堆或堆栈中分配了一个额外的数组插槽(或其他)。我想微软考虑过这个模糊的场景,但谁知道呢。关键是
Monitor.Enter
Monitor.Exit
呼叫应该总是成对的,这样你就不必担心了。

你为什么要这样做呢?什么都不会发生,工作会很顺利。但是为什么呢?+1个有趣的问题,尽管你可以想象,这将是你每个人在实践中都会做的事情。实际上我有一个对象字典和两个异步活动。一个可以添加或替换对象(旧对象被丢弃),另一个可以对对象执行一些操作。因此,我不希望在使用该对象时将其删除。但是,我认为更好的解决方案是为每个对象使用额外的同步对象。谢谢!所有答案都很好,但我选择了你的答案作为最详细的答案。