我有一个包含1000个对象的列表,每个对象都包含自己的LockObject。
我计划在多个线程上运行相同的东西。他们将从1,000个列表中选择6个随机对象,然后锁定其锁定对象。在发布它们之前先做些什么。
当我创建我希望使用该循环的6个对象的列表时,假设我按每个对象中的某个唯一值对列表进行排序,以下是否会避免2个或更多此类线程之间的死锁情况?
Players = Players.OrderBy(P => P.ID).ToList();
Lock(Players[0].LockObj)
Lock(Players[1].LockObj)
Lock(Players[2].LockObj)
Lock(Players[3].LockObj)
Lock(Players[4].LockObj)
Lock(Players[5].LockObj)
{
//...
}
我正在阅读这篇文章,试图弄清楚这是否是“正确”的方式来写这个,它似乎是,但是人们对死锁进行了警告。我相信这会避免死锁,但我不确定100%。
答案 0 :(得分:-1)
假设您按升序锁定,则不会出现死锁。你可以通过归纳证明这一点。
不会显示完整的教授,但这是一个想法。
给定n个锁的列表,如果你按升序锁定它们,那么锁定锁n
的任何线程总是能够完成,因为它是最后一个项目,它不会有等待之后的任何事情。
由于锁定n总是可以完成,因此锁定n-1也可以,因为锁定n是唯一可能等待的锁定n-1。
因此,通过感应锁定0-n将始终能够完成并且不能进行死锁。
答案 1 :(得分:-2)
由于您首先订购列表,所以不应该死锁,除非另一个线程试图从不同的代码中无序地锁定相同的对象。
在实际需要之前锁定对象通常是个坏主意。如果您正在执行需要同时访问所有六个对象的计算,那么很难解决这个问题,但如果您只是对六个对象中的每一个执行操作,则应该一次锁定一个。 / p>
for(int i = 0; i < 6; ++i)
{
lock(Players[i].LockObj)
{
//Do Stuff
}
}
如果您使用来自多个线程的此代码,您当前的代码可能会显着降低。如果不同的线程试图锁定同一个对象,你可能会遇到这样的情况,即每个线程只有一个线程停止等待轮到它们,导致同步行为缓慢。
如果您正在执行需要访问所有六个对象的计算,最好一次锁定一个,复制您需要的信息并解锁它们。然后进行计算,再次锁定一次,复制更改并解锁。
如果您的计算要求玩家不会更改复制数据时所处的状态,则可以在进行编辑时使用跟踪变量或哈希来检测数据何时被编辑,然后您可以选择策略处理这种情况:撤消已经对其他玩家进行的更改并重新计算?接受你是否覆盖了另一个线程的更改?做一些其他的行动?