当调用方法已经在同一对象上拥有锁时,试图获取该对象上的锁

本文关键字:对象 获取 调用 拥有 方法 | 更新日期: 2023-09-27 18:09:13

我有一些代码,我一直在学习系统,我遇到一些代码对我来说是一个代码气味,我不认为它会工作,但它确实。

我们有两个对象,对象A和对象B。对象A包含一个锁对象:

private object lockObj = new object();

对象B将获取对象A.lockObj上的锁,当B拥有锁时,它调用

A.SomeMethod();

A.SomeMethod()获取

this.lockObj

并在代码中显示:

ThreadTestOne:

 public class ThreadTestOne
{
    public object lockObject = new object();
    private List<string> lst;
    private ThreadTestTwo two;
    public List<string> Lst
    {
        get
        {
            return this.lst;
        }
        set
        {
            this.lst = value;
        }
    }
    public void Run()
    {
        lst = new List<string>();
        two = new ThreadTestTwo();
        two.Run(this);
    }
    public void End()
    {
        Console.WriteLine("ThreadTestOne.End");
        two.End();
    }
    public void LockMe()
    {
        Console.WriteLine("ThreadTestOne.LockMe");
        lock (this.lockObject)
            lst.Add("something");
            Thread.Sleep(500);
    }
}

ThreadTestTwo:

public class ThreadTestTwo
{
    private ThreadTestOne one;
    private Thread myThread;
    private bool ending = false;
    public void Run(ThreadTestOne a)
    {
        one = a;
        myThread = new Thread(new ThreadStart(Consume));
        Console.WriteLine("ThreadTestTwo Starting thread");
        myThread.Start();
    }
    public void End()
    {
        Console.WriteLine("ThreadTestTwo.End");
        ending = true;
        myThread.Join();
    }
    public void Consume()
    {
        while (!ending)
        {
            Console.WriteLine("ThreadTestTwo one.lockObject");
            lock (one.lockObject)
            {
                Console.WriteLine("two.LockMe");
                one.LockMe();
                one.Lst.Add("two");
                Thread.Sleep(500);
            }
        }
    }
}

当我查看上面的代码时,我认为one.LockMe()应该永远无法获得lockObj上的锁,因为ThreadTestTwo已经有锁了。

我认为这会导致死锁。但是,当我运行上面的示例代码时,它可以工作。此外,我正在审查的代码也可以工作,并且目前正在生产中。

这不会导致抛出异常的事实让我感到困惑。我假设这是个错误,对吗?

在我测试的代码中,最初只在两次尝试获取锁后读取数据,因此我认为编译器正在删除锁。

然而,我看了看MSIL,看到锁仍然在那里。

我的下一个想法是框架只是没有获取锁,因为我们只是在读取数据。

我在锁中添加了一个写操作,它仍然工作。然而,我可能并不完全理解锁是如何工作的。

尽管这是有效的,但我觉得这是错误的,我不能完全相信这不会在生产中引起问题。

我确实发现了这个问题:

在两个不同的代码块上使用相同的锁对象?

这是类似的,但我相信我的问题略有不同,我问的是锁定对象时,调用方法已经有一个锁在同一个对象上。

显然的代码,我有一个关于工作的问题,我想知道如何?

我假设这是错误的是错误的吗?

我注意到上面的代码有几个问题。

  1. 公共字段-我知道这是错误的,但这就是代码中的情况。
  2. 循环引用-我知道循环引用,知道为什么它不好。

感谢您提供的任何见解。

当调用方法已经在同一对象上拥有锁时,试图获取该对象上的锁

您似乎有这样的印象:拥有一个锁(即监视器)。但事实并非如此——一个线程拥有一个监视器。

. net中的监视器是可重入的——如果一个线程已经拥有监视器,它可以再次获得它。这将增加它的"锁计数"——当线程第一次释放监视器时,它只会减少锁计数,但由于该计数仍然是正的,因此没有其他线程能够获得监视器,直到原始线程再次释放它

Monitor.Enter (lock关键字调用的方法-它实际上调用TryEnter,但是…):

同一个线程多次调用Enter而不被阻塞是合法的;但是,在等待该对象的其他线程解除阻塞之前,必须调用相同数量的Exit调用。