1

我要求一段代码一次只能由一个线程运行(单个资源锁)。

C# 中的lock(object)语句允许这样做。但是,它不会保留对锁的请求顺序。

例如,考虑下面的 100 个线程开始,其中编号线程尝试按顺序锁定挂锁:

    for (int i = 0; i < 100; i++)
             {

            (new Thread(delegate(object index) 
               {
                  int name = (int) index;
                  byte[] len = new byte[2];

                  Console.WriteLine(string.Format("Thread:{0} going for lock. ",
                      name));
                  lock (padlock)
                  {

                     rnd.GetBytes(len);
                     ushort l = BitConverter.ToUInt16(len, 0);
                     Console.WriteLine(string.Format("Thread:{0} sleeping: {1}", 
                       name, l));
                     Thread.Sleep(l);
                  }
            })).Start(i);

访问的实际授予不是完美的顺序 (1->100) 或不是 FIFO。然而,似乎确实存在“早进早出”的 EIEO 模式(也许由堆运行?)。

问题是:什么决定了锁的授予顺序是否可以依靠它不饿死一个不幸的线程?

更新这个答案解释了它。这是相关的引用(Joe Duffy 在 Windows 上的并发编程):

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

4

2 回答 2

5

Servy 的回答当然是正确的。一些额外的细节:

什么决定了锁的授予顺序?

最后是操作系统。

可以依靠不饿死一个不幸的线程吗?

饥饿不太可能,但可能。如果你连一点饿死的机会都无法忍受,那么你将需要比锁更复杂的东西。

我还注意到锁是“不公平的”,因为你可以有一个线程在锁中,八个线程在等待,锁中的线程离开,第十个根本没有等待的线程请求锁并获得它,有效“断线”。Joe 给出了一个有趣的分析,为什么 Windows 现在在这里使用“不公平”的锁分配策略,如果你对这个主题感兴趣的话:

http://joeduffyblog.com/2006/12/14/anticonvoy-locks-in-windows-server-2003-sp1-and-windows-vista/

于 2013-05-21T20:29:47.213 回答
3

什么决定了锁的授予顺序

规范中没有定义顺序。你不能依赖任何命令。

它可以依靠不饿死一个不幸的线程吗?

不可以。如果这是您的应用程序中的潜在问题,您需要手动管理它。


另请注意,由于几个不同的原因,线程将运行的顺序未确定。您知道所有线程都将按特定顺序启动,但是一旦启动,您就不知道它们将如何安排。他们每个人都可以lock按任何顺序击中障碍物。即使根据他们击中的时间是 FIFO lock,它仍然是未定义的。

于 2013-05-21T20:13:34.273 回答