锁如何正常工作?

时间:2011-05-17 10:55:13

标签: c# .net synchronization locking thread-safety

我看到,对于使用非线程安全的对象,我们用这样的锁包装代码:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

那么当多个线程访问相同的代码时会发生什么(让我们假设它在ASP.NET Web应用程序中运行)。他们排队了吗?如果是这样,他们会等多久?

因使用锁而对性能产生什么影响?

10 个答案:

答案 0 :(得分:399)

lock语句由C#3.0翻译为以下内容:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

在C#4.0 this has changed中,它现在生成如下:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    // body
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(temp); 
    }
}

您可以找到有关Monitor.Enter here的更多信息。引用MSDN:

  

使用Enter获取监控器   作为参数传递的对象。如果   另一个线程执行了Enter   在对象上但尚未执行   相应的Exit,即当前   线程将阻塞直到另一个   线程释放对象。它是   合法的线程可以调用   Enter不止一次Exit   阻塞;但是,数量相等   之前必须调用Monitor.Enter个调用   等待对象的其他线程   将取消阻止。

{{1}}方法将无限期等待;它将超时。

答案 1 :(得分:238)

比你想象的要简单。

根据Microsoftlock关键字确保一个线程不进入代码的关键部分,而另一个线程处于临界区。如果另一个线程试图输入一个锁定的代码,它将等待,阻塞,直到该对象被释放。

lock关键字在块的开头调用Enter,在块的末尾调用Exitlock关键字实际上在后端处理Monitor类。

例如:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

在上面的代码中,首先线程进入临界区,然后它将锁定obj。当另一个线程试图进入时,它也会尝试锁定已被第一个线程锁定的obj。我将不得不等待第一个线程释放obj。当第一个线程离开时,另一个线程将锁定obj并进入临界区。

答案 2 :(得分:43)

不,他们没有排队,他们正在睡觉

表单的锁定声明

lock (x) ... 

其中x是引用类型的表达式,恰好等同于

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

你只需知道他们正在等待对方,只有一个线程会进入锁定区域,其他线程将等待......

监视器完全用.net编写,因此速度足够快,还可以class Monitor查看reflector以获取更多详细信息

答案 3 :(得分:26)

锁定将阻止其他线程执行锁定块中包含的代码。线程必须等到锁定块内的线程完成并释放锁定。这确实会对多线程环境中的性能产生负面影响。如果确实需要这样做,则应确保锁定块中的代码可以非常快速地处理。您应该尽量避免访问数据库等昂贵的活动。

答案 4 :(得分:10)

性能影响取决于您锁定的方式。您可以在此处找到一个很好的优化列表:http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

基本上你应该尝试尽可能少地锁定,因为它会让你的等待代码进入睡眠状态。如果您在锁中有一些繁重的计算或持久的代码(例如文件上传),则会导致巨大的性能损失。

答案 5 :(得分:7)

lock语句中的部分只能由一个线程执行,因此所有其他线程将无限期地等待持有锁完成的线程。这可能导致所谓的死锁。

答案 6 :(得分:7)

lock语句已转换为对MonitorEnterExit方法的调用。

lock语句将无限期地等待锁定对象被释放。

答案 7 :(得分:4)

lock实际上是隐藏Monitor类。

答案 8 :(得分:0)

根据Microsoft's MSDN,该锁等效于:

object __lockObj = x;
bool __lockWasTaken = false;
try
{
    System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
    // Your code...
}
finally
{
    if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}

顺便说一句,您可以使用DynaLock在运行时动态创建和管理锁!并使用上下文概念指定边界。

DynaLock是开源的,其源代码可在GitHub

获得。

答案 9 :(得分:-1)

lock阻止进入一个线程而另一个线程已经进入。通常,它用于多线程。我们可以说的最好的例子之一是“ Singleton”设计模式。

static Singleton instance = null;
static readonly object obj = new object();
    public static Singleton GetInstance
    {
        get
        {
            if (instance == null)
            {
                lock (obj)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
                return instance;


        }
    }