在没有死锁的情况下锁定两个对象
本文关键字:两个 对象 锁定 死锁 情况下 | 更新日期: 2023-09-27 18:21:28
我有一个问题在C++中很容易解决,但在C#中我仍然找不到一个好的解决方案:
我有一个函数foo,它需要锁定两个对象,这个函数可以用相反顺序的参数调用,例如:
static void foo(object o1, object o2)
{
lock (o1)
{
lock (o2)
{
...
}
}
}
static void bar(object a, object b)
{
ThreadPool.QueueUserWorkItem(s => foo(a, b));
ThreadPool.QueueUserWorkItem(s => foo(b, a));
}
这是一种打破僵局的方法。解决此问题的标准方法是始终以相同的顺序锁定对象。在C++中,我可以比较指针,但在"安全"C#中,我不知道任何方法,只知道一个非常丑陋的Monitor.TryEntry
解决方案(见下文)。还有更好的吗?请注意,对象是可变的,我不能依赖于Equals
、GetHashCode
、IComparable
。
static void foo(object o1, object o2)
{
const int Timeout = 1000;
while (true)
{
if (Monitor.TryEnter(o1, Timeout))
{
try
{
if (Monitor.TryEnter(o2, Timeout))
{
try
{
...
return;
}
finally
{
Monitor.Exit(o2);
}
}
}
finally
{
Monitor.Exit(o1);
}
}
}
}
如果您想要相同的有序锁定方法,您可以向用于锁定的对象添加一些属性,并基于它进行比较。您可以根据您的情况使用类或接口(即在锁定对象上实现IComparable
)。下面的示例使用具有Less
比较功能的类:
class ComparableLockingObject
{
static int LastOrderValue = 0;
private orderValue = LastOrderValue++;
public bool Less(ComparableLockingObject other)
{
return this.orderValue < other.orderValue;
}
}
static void foo(ComparableLockingObject o1, ComparableLockingObject o2,
Action action)
{
if (o2.Less(o1))
{
var temp = o1;
o1 = o2;
o2 = temp;
}
lock (o1)
{
lock (o2)
{
action();
}
}
}
您不需要锁定这些对象。您锁定了执行逻辑的代码:
static object lockObject = new object();
static void foo(object o1, object o2)
{
lock (lockObject)
{
// this code will be executed on one thread at the time. Why lock o1 & o2??
}
}