C#Lock语句

时间:2011-08-14 13:27:01

标签: c# locking mutex sync

当一个线程试图进入一个关键部分并获得一个锁时,它实际上在做什么?

我问这个是因为我经常创建一个对象(对象类型),它只用于锁定目的。 请考虑以下内容:我想编写一个接受集合的方法,以及一个将作为锁定对象的对象,以便该方法中的整个集合操作将在临界区内声明,该区域将被该给定对象锁定。 / p>

我应该使用“ref”传递该锁定对象还是传递该对象的引用副本就足够了?换句话说 - 因为lock语句仅用于引用类型,机制是否检查引用对象的值,还是检查指针的值?因为很明显,当传递一个没有“ref”的对象时,我实际上得到了一个引用的副本,而不是引用本身。

4 个答案:

答案 0 :(得分:6)

这是锁定时可以遵循的典型模式。基本上,您可以创建一个锁定对象,用于锁定对关键部分的访问(正如@Hans所说,它不保护您正在处理的对象 - 它只是处理锁定)。

class ThreadSafe
{
  static readonly object _locker = new object();
  static int _val1, _val2;

  static void Go()
  {
    lock (_locker)
    {
      if (_val2 != 0) Console.WriteLine (_val1 / _val2);
      _val2 = 0;
    }
  }
}

此示例来自线程上的Joseph Albahari's online book。它可以很好地概述您在创建lock语句时所发生的情况以及有关如何最佳地优化它的一些提示/技巧。绝对强烈推荐阅读。

Per Albahari再次将lock语句在.NET 4中翻译为:

bool lockTaken = false;
try
{
  Monitor.Enter (_locker, ref lockTaken);
  // Do your stuff...
}
finally { if (lockTaken) Monitor.Exit (_locker); }

它实际上比直接Monitor.Enter更安全,然后在Monitor.Exit中调用finally,这就是它在.NET 4中添加的原因。

答案 1 :(得分:3)

只需锁定object而不通过ref即可。 lock实际上做的是,打电话 在块的开头Monitor.Enter和退出时Monitor.Exit

希望这有帮助。

答案 2 :(得分:1)

MSDN说here关于锁定

  

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

这意味着它不是关于引用所指向的实际对象的引用或指针,因此您不需要传递ref简单传递引用将起作用

关于锁内部实际发生的事情,请参阅this问题的回答

“锁定语句由C#转换为以下内容:”

var temp = obj;

Monitor.Enter(temp);

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

答案 3 :(得分:1)

  

我应该使用“ref”传递该锁定对象还是传递该对象的引用副本就足够了?

可能都没有。如果您有一些非线程安全的资源,最好的选择通常是直接从一个类访问该资源,该类具有锁定对象作为字段(或者您可以直接锁定资源)。如果您将锁定对象传递给其他人,则很难确保代码仍能正常工作,例如锁定在应该完成时完成并且没有死锁。

但是如果你真的想要传递锁对象,就不需要像其他人指出的那样使用ref。锁定是在对象的实例上完成的,而不是在包含对它的引用的变量上完成的。