lock()保证是否按请求获得了?

Sam*_*eff 60 .net c# synchronization locking

当多个线程请求锁定同一个对象时,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

打印:

输入: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退出锁

Jon*_*eet 73

IIRC,它极有可能按顺序排列,但不能保证.我相信至少在理论上情况下,一个线程将被虚假地唤醒,注意它仍然没有锁定,并且转到队列的后面.这可能只适用于Wait/ Notify,但我也有一种潜在的怀疑,那就是锁定.

绝对不会依赖它 - 如果你需要按顺序发生事情,建立一个Queue<T>或类似的东西.

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

因为监视器在内部使用内核对象,所以它们表现出与OS同步机制也表现出的相同的粗略FIFO行为(在前一章中描述).监视器是不公平的,因此如果另一个线程试图在唤醒的等待线程尝试获取锁之前获取锁,则允许偷偷摸摸的线程获取锁.

"粗略FIFO"位是我之前想到的,"偷偷摸摸的线程"位进一步证明你不应该对FIFO排序做出假设.

  • 不会从多个线程向`Queue <T>`添加项目,无论如何都要求`lock`,因此出现完全相同的行为,因为出于完全相同的原因,项目可能会无序地添加到队列中? (3认同)
  • @Sam - 对此用例使用threadsafe`ConcurrentQueue <T>`http://msdn.microsoft.com/en-us/library/dd267265.aspx.对于有保证的顺序处理,请使用单个生产者线程和单个使用者线程. (2认同)
  • @Sam:两个线程之间会有一个竞争条件试图在同一时间添加一些东西,是的 - 但不同的是,一旦它们被添加,你可以获得有保证的订单......而在锁定的情况下,即使你能以某种方式告诉一个线程已经开始在另一个线程之前获得锁定方式,你也不能保证它实际上*先获取它. (2认同)

Bry*_*end 11

正常的CLR锁定不保证是FIFO.

但是,在这个答案中 有一个QueuedLock类,它将提供有保证的FIFO锁定行为.


Mic*_*urr 10

记录该lock语句是为了使用Monitor该类来实现它的行为,而Monitor类的文档没有提及(我可以找到)公平性.因此,您不应该依赖于请求顺序获取的请求锁.

事实上,杰弗里里希特的一篇文章表明其实lock并不公平:

当然 - 这是一篇旧文章所以事情可能已经发生了变化,但鉴于Monitor课堂合同中没有关于公平性的承诺,你需要假设最坏的情况.