【问题标题】:Lock on 2 objects togethere in c#在c#中一起锁定2个对象
【发布时间】:2013-10-03 01:09:20
【问题描述】:

有没有办法同时锁定 2 个不同的对象? 我尝试使用lock(obj1, obj2){...},但出现此错误:

invalid expression term ','

更新: 正如许多用户告诉我尝试只使用一个锁,我感谢他们的建议,因为大多数时候它更可取,我只想展示一个我认为对 2 个对象进行锁更合理的案例。 考虑在不同线程之间共享 2 个队列的情况。您需要避免同时执行Enqueue(item)Dequeue()。现在,在代码的特定部分,您希望从一个队列中获取一个元素并将其插入到第二个队列中。我知道可以这样做:

var itme;
lock(_lock1)
{
    item = q1.Dequeue();
    Monitor.Pulse(_lock1);
}
lock(_lock2)
{
    q2.Enqueue(item);
    Monitor.Pulse(_lock2);
}

但我认为锁定_lock1_lock2 更具可读性和简洁性。

【问题讨论】:

  • 为什么要锁定 2 个对象?这可能会导致死锁。
  • 我需要将currentIndexMessageList 锁定在许多线程中。
  • 你只需要一把锁。
  • 看起来您将尝试锁定一个值类型 (currentIndex)。这将锁定值的盒装版本(每次锁定时都会生成该框),因此它根本不会执行您期望的操作,允许锁定它的所有代码继续进行。
  • @SAM :lock 关键字需要一个引用类型。我怀疑currentIndex 是一个值类型。也许是 int 或 long。实际上,当您尝试锁定值类型时,编译器会抱怨。不要试图将此值强制为对象。听从@DanielAWhite 的建议。

标签: c# multithreading locking


【解决方案1】:

我强烈建议您使用一个锁来避免死锁,并且您应该锁定一个单独的引用,而不是您要修改的对象。

class MyClass
{
    private readonly object _lock = new object();
    private readonly List<int> _myList = new List<int>();
    private int _index;

    public void MyOperation()
    {
        lock(_lock) 
        {
            _index++;
        }
    }

    public void MyOperation2()
    {
        lock(_lock) 
        {
            _myList[_index] = 27;
        }
    }
}

【讨论】:

  • 你为什么说我应该避免锁定对象本身?
  • @SAM - .net 框架或其他代码可能 也会锁定该引用 - 这可能会导致死锁。
【解决方案2】:

lock 与两个(或更多)不同对象一起使用的语法如下。

lock (a) lock (b)
{
}

但是,通常不鼓励这种用法。这有几个原因。

  • 声明锁的顺序必须在各处保持一致,否则可能会发生死锁。
  • 几乎总是没有必要。
  • 这是一种代码味道。

在我看来,第一个原因确实是一种逃避。在开发过程中有无数种可能犯错误的方法。这只是众多之一。而且,事实上,纠正错误实际上很容易(相对于其他类型的错误)。只需确保始终以相同的顺序获取锁。另外,使用嵌套锁本身并没有什么危险(同样,只要正确完成)。 所有时间都获取嵌套锁。只是它几乎总是通过调用其他类或通过不同的堆栈帧进行执行来隐式完成。

这里真正重要的是第二个和第三个原因。在同一段代码中获取两个锁是不必要的,因为一个锁几乎总是足够的。事实上,我认为我从来不需要在同一段代码中获取两个锁。至少没有明确表示。这是一种代码味道,因为这通常意味着您对问题的思考是错误的。这可能意味着代码很复杂,或者您正在使用奇怪的代码路径。将代码结构化为只需要一把锁可能会使代码更容易理解。

另外,我也想知道您是否对锁的工作原理感到困惑。如果您认为lock 关键字中使用的对象实例受到保护而不会同时访问,那么您就错了。这不是lock 关键字的工作方式。 lock 引用的对象旨在标记代码的关键部分,该代码保证不会与标记为 same 对象引用的另一个(或相同)部分同时执行。

更新:

我看了你的例子,第一反应是这段代码可能不是线程安全的。我看不到所涉及的完整上下文。我所看到的是这个假设的类在内部使用了两个队列并且正在脉冲两个不同的锁。我可以做出一些推论,但它们可能是错误的。我很难想象你可能会在一个班级里做这样的事情。但是,话虽如此,您是否考虑过当执行此代码的线程在锁之间被抢占时会发生什么?这是您的班级进入半生不熟状态的机会(该项目不再在任何一个队列中)。当另一个线程试图使用这个类时会发生什么?它会优雅地处理物品悬而未决的情况吗?我必须查看更多代码才能进一步评论,但是看到锁的排列允许半生不熟的状态开始对我来说是一个巨大的危险信号。

【讨论】:

  • 感谢您的完整回答。正如您所说,似乎我认为对象lock 已打开,将受到保护,我真的这么认为。但我这么想的原因很简单:对于不同线程之间共享的对象,我在访问之前使用了lock。我同意你的观点,大多数时候,只有一个锁就足够了,但我会给你举一个例子,我认为有必要锁定 2 个对象。
  • @SAM:是的,用示例更新您的问题,以便每个人都可以查看它。
  • 考虑您想要保护对 2 个不同队列的访问的情况。一旦您想从其中一个元素中删除一个元素并将该元素插入另一个元素。这可以一次只使用一个锁,我们可以为第一个队列做一个锁,从中删除元素,释放锁,获得第二个队列的锁,将元素插入其中并继续。但我认为这不是一个漂亮的代码。不太可读。
【解决方案3】:

试试这个

lock(obj1)
{
   lock(obj2)
   {
        // here you have lock on obj1 and obj2
   }
}

lock 只接受一个参数。上面的示例将锁定第二个锁定范围内的两个项目。

也许它提供了问题的答案,但是:

在这里锁定并不意味着在锁定期间没有其他代码 在其他线程上可以访问或修改对象。如果你锁定一个对象 任何其他线程都可以同时修改对象。什么锁 代码块允许你做的就是让代码在锁块里面 成为单项,即只有一个线程可以执行锁定代码块 一次,其他尝试执行相同代码块的线程将 必须等到所有者线程完成执行代码 堵塞。所以基本上你真的不需要锁定 2 个或更多对象 通常的情况。通过锁定您的目的是使代码块单一 进入

简而言之,正如其他人提到的那样,这个解决方案是无用的,更好的是创建锁定所需的新对象。

【讨论】:

  • “这里你锁定了 obj1 和 obj2” - 不,你什么都没锁定。您已锁定 2 个对象并引发死锁。
  • 你能提供一些关于 locked onlocked @HenkHolterman 的解释吗?
  • @SAM - lock(x) 不会以任何方式改变x 的状态,它只会影响运行它的线程。
猜你喜欢
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多