锁如何正常工作?

NLV*_*NLV 482 .net c# synchronization locking thread-safety

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

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}
Run Code Online (Sandbox Code Playgroud)

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

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

Ste*_*ven 421

lock声明由C#3.0翻译为以下内容:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}
Run Code Online (Sandbox Code Playgroud)

在C#4.0中,这已经改变,现在生成如下:

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

你可以找到什么更多信息Monitor.Enter确实在这里.引用MSDN:

使用Enter以获取作为参数传递的对象上的监视器.如果另一个线程已经Enter 对该对象执行了一个但尚未执行相应Exit的线程,则当前线程将阻塞,直到另一个线程释放该对象.Enter如果没有阻塞,同一个线程不止一次调用是合法的 ; 但是,Exit在等待对象的其他线程将解除阻塞之前,必须调用相同数量的 调用.

Monitor.Enter方法将无限期等待; 它不会超时.

  • @priehl它允许用户在没有整个系统的情况下将`obj`更改为死锁. (10认同)
  • 根据MSDN"使用锁定(C#)或SyncLock(Visual Basic)关键字通常比直接使用Monitor类更受欢迎,因为锁定或SyncLock更简洁,并且因为锁定或SyncLock确保底层监视器被释放,甚至如果受保护的代码抛出异常.这是通过finally关键字完成的,该关键字执行其关联的代码块,无论是否抛出异常.http://msdn.microsoft.com/en-us/library/ms173179.aspx (9认同)
  • var temp = obj的重点是什么?线.因为它只是一个参考开始,有什么好处让另一个人做? (9认同)
  • @Joymon最终,每个语言特征都是语法糖.语言功能的关键在于提高开发人员的工作效率,使应用程序更易于维护,锁定功能也是如此. (6认同)
  • 正确.这是`lock`-statement和Monitor的全部目的:这样你就可以在一个线程中执行一个操作,而不必担心另一个线程搞砸了. (2认同)

Uma*_*bas 257

它比你想象的简单.

微软称:该lock关键字确保一个线程不进入代码的关键部分而另一个线程处于关键部分.如果另一个线程试图输入锁定的代码,它将等待,阻止,直到该对象被释放.

lock关键字要求Enter在该块的开始和Exit在块的结尾.lock关键字实际上处理Monitor后端的类.

例如:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}
Run Code Online (Sandbox Code Playgroud)

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

  • @batmaci - 锁定一个单独的私有虚拟对象可以保证没有其他人锁定该对象.如果您锁定数据并且外部可以看到同一条数据,则会失去该保证. (8认同)
  • 如果有多个进程等待锁定释放会发生什么?等待进程是否排队,以便它们按FIFO顺序锁定临界区? (7认同)
  • 我们应该创建一个虚拟对象来锁定,还是可以锁定上下文中的现有变量? (5认同)

Ars*_*yan 45

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

表单的锁定声明

lock (x) ... 
Run Code Online (Sandbox Code Playgroud)

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

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }
Run Code Online (Sandbox Code Playgroud)

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

监视器完全写在.net中,所以它足够快,还可以查看 具有反射器的类监视器以获取更多详细信息

  • 请注意,为`lock`语句发出的代码在C#4中略有改变:http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not- mix.aspx (6认同)

And*_*rew 27

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


Sim*_*ker 10

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

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

  • 但是尝试编写低锁代码通常会导致微妙的、难以发现和修复的错误,即使您是该领域的专家也是如此。使用锁通常是两害相权取其轻。您应该准确锁定所需的数量,不多也不少! (2认同)

Mr4*_*r47 7

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


Pao*_*sco 7

lock声明被转换为调用EnterExit方法Monitor.

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