同一对象上的多个锁是否存在死锁情况?

时间:2017-09-18 18:19:23

标签: c# multithreading locking task

我们有一个代码,在不同的方法中有两个锁定在同一个对象上。两种方法都可以从不同的线程调用。

这会陷入僵局吗? 我们应该在两种方法中使用相同的lock1吗?

使用两个不同锁的原因是从并行运行的各种任务中删除,但更新列表发生在每隔几秒运行一次的新线程中。

private static Object Lock1 = new Object();
private static Object Lock2 = new Object();
private static List<string> list = new List<string>();

public void Method1()
{
  lock(Lock1)
  {
   //update list
  }
}

public void Method2()
{
  lock(Lock2)
  {
    //remove from list
  }   
}

更新

感谢您的健康讨论,我已更新我的代码以使用.net提供的线程安全集合BlockingCollection。我继续使用lock1,因为我需要控制列表可以包含多少个对象。我已经删除了lock2,因为它不需要线程安全列表。

2 个答案:

答案 0 :(得分:4)

  

这会陷入僵局吗?

没有

  

我们应该在两种方法中使用相同的lock1吗?

是。在访问多个线程上的特定对象时,应始终锁定相同的锁定对象。在您的特定情况下,您显示的代码完全错误且已损坏。每个列表都应该有一个锁 ,并且每次访问列表时都会一直锁定

如果您要更新列表变量,那么同样的事情 - 您应该为列表变量设置一个锁,并且每次您出于任何原因访问变量 需要在该锁定下。

  

使用两个不同锁的原因是从并行运行的各种任务中删除,但更新列表发生在每隔几秒运行一次的新线程中。

那没关系。 所有更新,无论是删除还是其他更新,都必须在同一锁定下进行

如果您处于许多常用读者和少数作家的情况下,则会针对这些情况使用专用锁。但那不是你的情景。

你没有问过一些问题:

  

导致死锁的原因是什么?

void Transfer(Account a1, Account a2, decimal amount)
{
  lock(a1)
  {
    lock(a2)
    {
      // do the transfer
    }
  }
}

假设线程X调用Transfer(savings, checking, 100)并获取锁定savings。然后我们切换到调用Transfer(checking, savings, 50)的线程Y.我们锁定checking,然后尝试锁定savings,但不能,因为X拥有它。然后我们切换回X,它试图锁定checking,但不能,因为Y有它。然后我们永远等待。那是一个僵局。

  

将同一对象“嵌套”锁定在同一个线程上会导致死锁吗?

没有。答案说错了。锁定你已经拥有的线程会自动成功。

  

我应该使用更好的技术吗?

是。多线程程序很难正确编写。如果必须,请使用高级对象,例如多线程集合或不可变集合,这些对象旨在有效地解决这些问题,而无需显式锁定。

您还应该阅读

Confusion about the lock statement in C#

答案 1 :(得分:3)

这不会死锁,除非有一些东西从注释掉的部分调用到另一个方法中,因此可能导致一个线程有第一个锁并等待第二个锁,而另一个有第二个锁的情况等待第一次。

这里的一个大问题是锁没有保护列表。首先需要锁定的原因是value不是为并发使用而设计的,因此您需要序列化访问。由于添加和删除方法都涉及复制数组中的元素,维护计数并将一个内部交换为另一个内部超过其容量,因此有很多机会同时添加和删除无法添加,无法删除,搞乱内部计数,或者有一个神秘添加的null或删除的东西,不应该。更一般地说,甚至没有保证它不会被置于其他代码假设的状态是不可能的,并在以后导致奇怪的错误。

您需要在两种方法中保护列表不受任何方法的影响,因此需要使用相同的锁。