【问题标题】:Are threads waiting on a lock FIFO?线程是否在等待锁定 FIFO?
【发布时间】:2009-08-25 20:41:58
【问题描述】:

假设我有以下代码

static class ...
{
    static object myobj = new object();

    static void mymethod()
    {
        lock(myobj)
        {
            // my code....
        }
    }
}

那么假设线程1 有锁,线程2 尝试运行mymethod。 是等待锁释放还是抛出异常?

如果它确实等待,是否确保顺序,以便如果有其​​他线程进入它们是 FIFO?

【问题讨论】:

    标签: c# .net multithreading


    【解决方案1】:

    更新了我的答案: 它们在排队,但不保证顺序是先进先出的。

    查看此链接:http://www.albahari.com/threading/part2.aspx

    【讨论】:

    【解决方案2】:

    从您的代码中不清楚myobj 如何在mymethod 中可见。看起来var myobj 是声明范围内的局部堆栈变量(因为是var)。在这种情况下,可能每个线程都有一个单独的实例,mymethod 不会阻塞。

    更新

    关于整个 FIFO 参数,需要一些背景信息:CLR 不提供同步。正是 CLR host 将此作为 服务 提供给 CLR 运行时。主机实现IHostSyncManager 和其他接口并提供各种同步原语。这似乎无关紧要,因为最常见的主机是典型的应用程序主机(即您编译成和 exe),这会将所有同步延迟到操作系统(Win32 API 中的旧 Petzold 书原语)。然而,至少还有两个主要的托管环境:ASP.Net 一个(我不确定这是做什么的)和 SQL Server。我可以肯定的是,SQL Server 在SOS (基本上是用户更多的操作系统)之上提供了所有原语,从不触及操作系统原语,并且 SOS 原语在设计上是不公平的,以避免锁车队(即保证没有先进先出)。正如另一个响应中的链接已经指出的那样,操作系统原语也开始提供不公平的行为,与避免锁护卫队的原因相同。

    有关锁定车队的更多信息,您应该阅读 Rick Vicik 的文章Designing Applications for High Performance

    锁定车队

    FIFO 锁保证公平和 以牺牲进步为代价 造成锁车队。术语 原意是几个线程 执行与代码相同的部分 导致更高碰撞的组 比如果它们是随机分布的 在整个代码中(很像 汽车被分组 通过红绿灯)。特别的 我说的现象更糟 因为一旦它形成了隐含的 锁所有权的交接保持 锁步中的线程。

    为了说明,考虑这个例子 一个线程持有一个锁并且它 持有锁时被抢占。 结果是所有其他线程 将堆积在等待名单上 锁。当被抢占线程(锁 所有者此时)再次运行 并释放锁,它 自动交出所有权 锁定到等待的第一个线程 列表。该线程可能无法运行 一些时间,但“保持时间”时钟 滴答作响。前任主人 通常在之前再次请求锁 候补名单被清空, 使车队永存

    【讨论】:

    • 你说的没错,就是静态对象中的静态对象,我写得太快了。现在会修复它
    【解决方案3】:

    一个简单的例子告诉我们,顺序不保证是先进先出

        using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Diagnostics;
    
    
    namespace ConsoleApplication
    {
        class Program
        {
            private static Info info = new Info();
    
            static void Main(string[] args)
            {
                Thread[] t1 = new Thread[5];
                for (int i = 0; i < 5; i++)
                {
                    t1[i] = new Thread(info.DoWork);
                }
    
                Thread[] t2 = new Thread[5];
                for (int i = 0; i < 5; i++)
                {
                    t2[i] = new Thread(info.Process);
                }
    
                for (int i = 0; i < 5; i++)
                {
                    t1[i].Start();
                    t2[i].Start();
                }
    
                Console.ReadKey();
            }
        }
    
        class Info
        {
            public object SynObject = new object();
    
            public void DoWork()
            {
                Debug.Print("DoWork Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
                lock (this.SynObject)
                {
                    Debug.Print("Thread Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(5000);
                    Debug.Print("Thread Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
                }
            }
    
            public void Process()
            {
                Debug.Print("Process Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
                lock (this.SynObject)
                {
                    Debug.Print("Process Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(5000);
                    Debug.Print("Process Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
                }
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Diagnostics;
    
    
    namespace ConsoleApplication
    {
        class Program
        {
            private static Info info = new Info();
    
            static void Main(string[] args)
            {
                Thread[] t1 = new Thread[5];
                for (int i = 0; i < 5; i++)
                {
                    t1[i] = new Thread(info.DoWork);
                }
    
                Thread[] t2 = new Thread[5];
                for (int i = 0; i < 5; i++)
                {
                    t2[i] = new Thread(info.Process);
                }
    
                for (int i = 0; i < 5; i++)
                {
                    t1[i].Start();
                    t2[i].Start();
                }
    
                Console.ReadKey();
            }
        }
    
        class Info
        {
            public object SynObject = new object();
    
            public void DoWork()
            {
                Debug.Print("DoWork Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
                lock (this.SynObject)
                {
                    Debug.Print("Thread Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(5000);
                    Debug.Print("Thread Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
                }
            }
    
            public void Process()
            {
                Debug.Print("Process Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
                lock (this.SynObject)
                {
                    Debug.Print("Process Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(5000);
                    Debug.Print("Process Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
                }
            }
        }
    }
    

    执行会像这样进行

    Process Lock Reached: 15
    Process Lock Enter: 15
    DoWork Lock Reached: 12
    Process Lock Reached: 17
    DoWork Lock Reached: 11
    DoWork Lock Reached: 10
    DoWork Lock Reached: 13
    DoWork Lock Reached: 9
    Process Lock Reached: 18
    Process Lock Reached: 14
    Process Lock Reached: 16
    Process Lock Exit: 15
    Thread Lock Enter: 9
    Thread Lock Exit: 9
    Process Lock Enter: 14
    Process Lock Exit: 14
    Thread Lock Enter: 10
    Thread Lock Exit: 10
    Thread Lock Enter: 11
    Thread Lock Exit: 11
    Process Lock Enter: 16
    Process Lock Exit: 16
    Thread Lock Enter: 12
    Thread Lock Exit: 12
    Process Lock Enter: 17
    Process Lock Exit: 17
    Thread Lock Enter: 13
    Thread Lock Exit: 13
    Process Lock Enter: 18
    Process Lock Exit: 18
    

    如您所见,到达锁的过程与锁进入的过程不同。

    【讨论】:

      【解决方案4】:

      Windows 和 CLR 尽力保证等待的公平性(FIFO 顺序)。但是,在某些情况下,等待锁的线程的顺序是可以更改的,主要围绕警报等待和所有 CLR 线程锁定将线程置于警报状态。

      出于所有实际目的,您可以假设订单是先进先出的;但是,请注意这个问题。

      【讨论】:

        【解决方案5】:

        它会等待,它们的顺序不会相同。

        根据您的需要,如果您查看ReaderWriterLock 之类的内容或不只是lock 的内容,您可能会获得更高的性能

        【讨论】:

          猜你喜欢
          • 2022-11-17
          • 1970-01-01
          • 1970-01-01
          • 2017-08-26
          • 2018-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-05-24
          相关资源
          最近更新 更多