一尘不染

lock()是否保证按请求的顺序获取?

c#

当多个线程请求对同一对象的锁时,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出口锁


阅读 420

收藏
2020-05-19

共1个答案

一尘不染

IIRC, 很有可能
按照该顺序进行,但不能保证。我认为,至少在理论上有一个线程会被虚假唤醒的情况,请注意该线程仍然没有锁,然后转到队列的后面。可能仅用于Wait/
Notify,但我有一个偷偷的怀疑,它也用于锁定。

绝对 不会依赖它-如果您需要按顺序进行操作,构建一个Queue<T>或类似的东西。

编辑:我刚刚在Windows上的
Joe
Duffy的并发编程中找到了这个,它基本上同意:

由于监视器在内部使用内核对象,因此它们表现出与OS同步机制同样表现出的大致FIFO行为(在上一章中进行了介绍)。监视器是不公平的,因此,如果在唤醒的等待线程尝试获取锁之前另一个线程尝试获取锁,则允许偷偷摸摸的线程获取锁。

“大致FIFO”位是我以前想到的,“鬼thread线程”位进一步证明了您不应该对FIFO顺序进行假设。

2020-05-19