我想看看如果更改由 Monitor.Enter() 独占锁定的对象的引用会发生什么。正如预期的那样,抛出了 SynchronizationLockException。但是我很惊讶地看到在抛出异常之前有几个线程通过了监视器。
下面是代码的作用。
- 创建并启动 100 个线程
- 让所有线程等待,直到设置了 ManualResetEvent。
- 设置 ManualResetEvent - 有点像在 Indy 比赛中挥动绿旗。
- 在 x 上设置独占锁 (Monitor.Enter(x))
- 更改 x 的引用。
此时我预计会抛出某种异常,但直到 Monitor.Exit(x) 才发生。真正奇怪的是,在引发异常之前,似乎有 10 - 20 个线程能够越过锁。这是怎么发生的?似乎不应该。当然,更改独占锁定对象的引用是不行的。我永远不会在真正的代码中这样做,但我仍然很惊讶地看到其他线程通过了监视器。你的想法?
using System;
using System.Threading;
namespace ThreadingPlayground
{
class Program
{
private int _value;
object x = new object();
ManualResetEvent _event = new ManualResetEvent(false);
static void Main()
{
Program p = new Program();
p.StartThreads();
Console.ReadLine();
}
private void StartThreads()
{
for(int i = 0;i<100;i++)
{
Thread t = new Thread(IncrementValue);
t.Start();
}
_event.Set();
}
private void IncrementValue()
{
WaitHandle.WaitAll(new WaitHandle[] {_event});
Monitor.Enter(x);
// Change the reference of the exclusively locked object. This
// allows other threads to enter, should this be possible?
x = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine(++_value);
// throws a SynchronizationLockException
// but not until 10 - 20 more lines are written
Monitor.Exit(x);
}
}
}
请您参考如下方法:
您看到的是预期行为。用于将引用传递给 Monitor.Enter() 的实际变量没有什么特别之处。更改引用不应阻止其他线程获取独占锁,因为变量具有新值,并且该引用未在任何地方被锁定。
您的问题与 Exit 相关,因为调用 Exit 的线程没有对传入的引用进行独占锁定。另一个线程很可能对它,但您正在执行的线程没有。
如您所知,这就是为什么始终最好使用其引用永远不会更改的变量进行锁定的原因。如果您的资源变量可能发生变化,请使用新的引用。
够清楚了吗?
