我们有一个代码,在不同的方法中有两个锁定在同一个对象上。两种方法都可以从不同的线程调用。
这会陷入僵局吗? 我们应该在两种方法中使用相同的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,因为它不需要线程安全列表。
答案 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有它。然后我们永远等待。那是一个僵局。
将同一对象“嵌套”锁定在同一个线程上会导致死锁吗?
没有。答案说错了。锁定你已经拥有的线程会自动成功。
我应该使用更好的技术吗?
是。多线程程序很难正确编写。如果必须,请使用高级对象,例如多线程集合或不可变集合,这些对象旨在有效地解决这些问题,而无需显式锁定。
您还应该阅读
答案 1 :(得分:3)
这不会死锁,除非有一些东西从注释掉的部分调用到另一个方法中,因此可能导致一个线程有第一个锁并等待第二个锁,而另一个有第二个锁的情况等待第一次。
这里的一个大问题是锁没有保护列表。首先需要锁定的原因是value
不是为并发使用而设计的,因此您需要序列化访问。由于添加和删除方法都涉及复制数组中的元素,维护计数并将一个内部交换为另一个内部超过其容量,因此有很多机会同时添加和删除无法添加,无法删除,搞乱内部计数,或者有一个神秘添加的null或删除的东西,不应该。更一般地说,甚至没有保证它不会被置于其他代码假设的状态是不可能的,并在以后导致奇怪的错误。
您需要在两种方法中保护列表不受任何方法的影响,因此需要使用相同的锁。