假设我有以下课程:
public class IntBagWithLock
{
private readonly lockObject = new object();
private bool assigned = false;
private int data1;
private int data2;
public int? Data1
{
get { lock (lockObject) { return assigned ? data1 : (int?)null; } }
}
public int? Data2
{
get { lock (lockObject) { return assigned ? data2 : (int?)null; } }
}
public bool Assigned { get { lock(lockObject) { return assigned; } }
public bool TrySetData(int value1, int value2)
{
lock (lockObject)
{
if (assigned) return false;
data1 = value1;
data2 = value2;
assigned = true;
return true;
}
}
public bool IsEquivalentTo(IntBagWithLock other)
{
if (ReferenceEquals(this, other)) return true;
if (ReferenceEquals(other, null)) return false;
lock (lockObject)
{
if (!assigned) return false;
lock (other.lockObject)
{
return other.assigned && other.data1 == data1 && other.data2 == data2;
}
}
}
}
我担心的问题是,由于IsEquivalentTo
的实现方式,如果线程调用item1.IsEquivalentTo(item2)
并获得item1
,我可能会遇到死锁情况。 }锁定,另一个调用item2.IsEquivalentTo(item1)
并获得item2
。
我应该尽可能地确保不会发生此类死锁?
更新2 :代码示例已被修改为更接近我实际拥有的内容。我认为所有答案仍然有效。
答案 0 :(得分:2)
通常,您为每个对象提供唯一ID,然后从较低ID锁定到较高ID:
public class BagWithLock
{
// The first Id generated will be 1. If you want it to be 0, put
// here -1 .
private static int masterId = 0;
private readonly object locker = new object();
private readonly int id = Interlocked.Increment(ref masterId);
public static void Lock(BagWithLock bwl1, BagWithLock bwl2, Action action)
{
if (bwl1.id == bwl2.id)
{
// same object case
lock (bwl1.locker)
{
action();
}
}
else if (bwl1.id < bwl2.id)
{
lock (bwl1.locker)
{
lock (bwl2.locker)
{
action();
}
}
}
else
{
lock (bwl2.locker)
{
lock (bwl1.locker)
{
action();
}
}
}
}
}
你可以像使用它一样:
bool equals;
BagWithLock(bag1, bag2, () => {
equals = bag1.SequenceEquals(bag2);
});
所以你传递Action
包含你想要在lock
内做的事情。
Interlocked.Increment
上的static masterId
保证每个班级都有唯一的id
。请注意,如果您创建此类的超过40亿个实例,那么将成为问题。如果您需要,请使用long
。
答案 1 :(得分:1)
我不知道为什么每当你达到或等于你时就会锁定,但你可以这样做:
public bool IsEquivalentTo(BagWithLock other)
{
object myData;
object otherData;
lock (lockObject)
myData = data;
lock (other.lockObject)
otherData = other.data;
return object.Equals(myData, otherData);
}
这样,物品在比较时不会改变。
一般来说,这种锁有一些缺点,我认为我会做一般的静态lockObject
所以你只能在一个可能是竞争条件的方法中使用对象< / p>
<强>更新强> 根据你的更新,我会说你应该使用:
private static readonly object equalLock = new object();
public bool IsEquivalentTo(IntBagWithLock other)
{
lock(equalLock){
if (ReferenceEquals(this, other)) return true;
if (ReferenceEquals(other, null)) return false;
if (!assigned) return false;
return other.assigned && other.data1 == data1 && other.data2 == data2;
}
}
答案 2 :(得分:1)
由于OP提到data
是Immutable
,我认为这里根本不需要锁,“volatile”应该可以解决问题。
public class BagWithLock
{
private volatile object data;
public object Data
{
get { lock return data; }
set { data = value; }
}
public bool IsEquivalentTo(BagWithLock other)
{
return object.Equals(data, other.data);
}
}
这应该是线程安全的。如果我错了请纠正我。
答案 3 :(得分:0)
也许您可以使用成本更高,WaitHandle
派生的锁定对象(例如Mutex),并在需要多次锁定时使用WaitHandle.WaitAll()
?