我有一些代码,我一直在学习系统,我遇到了一些代码,对我来说是一个代码味道,我认为它根本不会起作用,但确实如此。
我们有两个对象,对象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
并看到锁仍在那里。
我的下一个想法是框架只是没有获取锁,因为我们只是在阅读数据。
我在锁中添加了一个写操作,它仍然有效。但是,我可能无法完全理解锁定工作的方式。
尽管这有效,但我觉得这是错误的,我并不完全相信这不会导致生产问题。
我确实发现了这个问题:
use the same lock object at two different code block?
哪个相似,但我相信我的问题略有不同,我问的是当调用方法已经锁定同一个对象时锁定一个对象。
显然代码我对作品有疑问,我想知道怎么做?
我认为这是错误的吗?
上面的代码中我注意到了一些问题。
感谢您提供的任何见解。
答案 0 :(得分:3)
您似乎认为类拥有锁(又称监视器)。情况并非如此 - 线程拥有一台监视器。
.NET中的监视器是可重入的 - 如果一个线程已经拥有监视器,它可以再次获取它。这将增加它的“锁定计数” - 当线程第一次释放监视器时,它只会减少锁定计数,但由于计数仍然是正数,因此没有其他线程可以获取监视器,直到原始线程再次发布 。
来自Monitor.Enter
(lock
关键字排序调用的方法 - 实际上调用TryEnter
,但是......):
同一个线程在没有阻塞的情况下不止一次调用
Enter
是合法的;但是,在等待对象的其他线程将解除阻塞之前,必须调用相同数量的Exit
调用。