线程在锁定FIFO上等待吗?

时间:2009-08-25 20:41:58

标签: c# .net multithreading

假设我有以下代码

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

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

然后让我们说当thread1有锁定时,thread2尝试运行mymethod。 是否会等待释放锁定或抛出异常?

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

5 个答案:

答案 0 :(得分:8)

更新了我的回答: 它们排队,但订单不保证是FIFO。

点击此链接:http://www.albahari.com/threading/part2.aspx

答案 1 :(得分:3)

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

<强>更新

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

有关锁定车队的更多信息,请阅读Designing Applications for High Performance上的Rick Vicik文章:

  

锁定车队

     

FIFO锁定保证了公平性   以牺牲为代价推进进步   导致锁定车队。术语   最初意味着几个线程   执行代码的相同部分   一组导致更高的碰撞   而不是他们是随机分布的   整个代码(很像   汽车被分组成包   乘坐交通灯)。特别的   我说的现象更糟糕   因为一旦它形成了隐含的   锁定所有权的交接保持了   锁定步骤中的线程。

     

为了说明,请考虑这个例子   线程持有锁的地方   在拿着锁的时候被抢先一步。   结果是所有其他线程   将堆积在等待名单上   锁。当被抢占的线程(锁定   此时所有者)再次开始运行   并释放锁,它   自动掌握所有权   锁定等待的第一个线程   名单。该线程可能无法运行   一段时间,但“保持时间”的时钟   正在滴答作响。以前的老板   通常在再次请求锁定之前   等待清单被清除,   使车队永久化

答案 2 :(得分:1)

一个简单的例子告诉我们,订单不能保证是FIFO

    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

正如您所看到的那样,锁定输入的过程与锁定不同。

答案 3 :(得分:0)

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

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

答案 4 :(得分:-1)

它会等待,它们的顺序不一样。

根据您的需求,如果您查看ReaderWriterLocklock以外的其他内容

,您可能会获得更高的效果