当多个线程请求对同一对象的锁时,CLR是否保证将按请求的顺序获取锁?
我写了一个测试,看这是否正确,似乎表明是,但是我不确定这是否是确定的。
class LockSequence { private static readonly object _lock = new object(); private static DateTime _dueTime; public static void Test() { var states = new List<State>(); _dueTime = DateTime.Now.AddSeconds(5); for (int i = 0; i < 10; i++) { var state = new State {Index = i}; ThreadPool.QueueUserWorkItem(Go, state); states.Add(state); Thread.Sleep(100); } states.ForEach(s => s.Sync.WaitOne()); states.ForEach(s => s.Sync.Close()); } private static void Go(object state) { var s = (State) state; Console.WriteLine("Go entered: " + s.Index); lock (_lock) { Console.WriteLine("{0,2} got lock", s.Index); if (_dueTime > DateTime.Now) { var time = _dueTime - DateTime.Now; Console.WriteLine("{0,2} sleeping for {1} ticks", s.Index, time.Ticks); Thread.Sleep(time); } Console.WriteLine("{0,2} exiting lock", s.Index); } s.Sync.Set(); } private class State { public int Index; public readonly ManualResetEvent Sync = new ManualResetEvent(false); } }
印刷品:
进入:0 0锁 0睡眠49979998个滴答 进入:1 进入:2 进入:3 进入:4 进入:5 进入:6 进入:7 进入:8 进入:9 0退出锁 1锁 1睡5001滴答 1个出口锁 2锁 2睡觉5001滴答 2出口锁 3锁 3睡觉5001滴答 3出口锁 4锁 4睡觉5001滴答 4出口锁 5锁 5睡觉5001滴答 5出口锁 6锁 6退出锁 7锁 7退出锁 8锁 8出口锁 9锁 9出口锁
进入:0
0锁
0睡眠49979998个滴答
进入:1
进入:2
进入:3
进入:4
进入:5
进入:6
进入:7
进入:8
进入:9
0退出锁
1锁
1睡5001滴答
1个出口锁
2锁
2睡觉5001滴答
2出口锁
3锁
3睡觉5001滴答
3出口锁
4锁
4睡觉5001滴答
4出口锁
5锁
5睡觉5001滴答
5出口锁
6锁
6退出锁
7锁
7退出锁
8锁
8出口锁
9锁
9出口锁
IIRC, 很有可能 按照该顺序进行,但不能保证。我认为,至少在理论上有一个线程会被虚假唤醒的情况,请注意该线程仍然没有锁,然后转到队列的后面。可能仅用于Wait/ Notify,但我有一个偷偷的怀疑,它也用于锁定。
Wait
Notify
我 绝对 不会依赖它-如果您需要按顺序进行操作,构建一个Queue<T>或类似的东西。
Queue<T>
编辑:我刚刚在Windows上的 Joe Duffy的并发编程中找到了这个,它基本上同意:
由于监视器在内部使用内核对象,因此它们表现出与OS同步机制同样表现出的大致FIFO行为(在上一章中进行了介绍)。监视器是不公平的,因此,如果在唤醒的等待线程尝试获取锁之前另一个线程尝试获取锁,则允许偷偷摸摸的线程获取锁。
“大致FIFO”位是我以前想到的,“鬼thread线程”位进一步证明了您不应该对FIFO顺序进行假设。