【发布时间】:2015-03-03 19:30:46
【问题描述】:
我正在尝试使用 C# 中的线程,结果我创建了以下类。我试图避免任何竞争条件的情况,但在使用时发生了死锁。
该类使用两种不同的锁,一个用于直接操作的自旋锁,另外一个Monitor 锁用于等待没有准备好的对象。我最初使用EventWaitHandle,但发现由于WaitOne/Set 的优先级,竞争条件是不可避免的。
注意Monitor.Pulse 不能在Monitor.Wait 之前,那么还有什么可能导致死锁呢?在 5 个线程使用容量为 4 的TestPool 类的情况下,死锁总是在不规则的时刻出现在SpinLock。
internal class TestPool<T> where T : class
{
private int capacity;
private int unitPos;
private int waitUnitPos;
private int waitCount;
private int lockState;
private object lockObj;
private T[] units;
private Func<T> unitFactory;
public TestPool(int capacity, Func<T> unitFactory)
{
this.lockObj = new object();
this.unitFactory = unitFactory;
Init(capacity);
}
public T Fetch()
{
T unit;
Lock();
unit = (unitPos != capacity) ? units[unitPos++] : Wait();
Unlock();
return unit;
}
public void Store(T unit)
{
Lock();
if (waitCount == 0)
{
units[--unitPos] = unit;
}
else
{
Pulse(unit);
}
Unlock();
}
private T Wait()
{
waitCount++;
lock (lockObj)
{
Unlock();
Monitor.Wait(lockObj);
Lock();
return units[--waitUnitPos];
}
}
private void Pulse(T unit)
{
waitCount--;
units[waitUnitPos++] = unit;
lock (lockObj)
{
Monitor.Pulse(lockObj);
}
}
private void Lock()
{
if (Interlocked.CompareExchange(ref lockState, 1, 0) != 0)
{
SpinLock();
}
}
private void SpinLock()
{
SpinWait spinWait = new SpinWait();
do
{
spinWait.SpinOnce();
}
while (Interlocked.CompareExchange(ref lockState, 1, 0) != 0);
}
private void Unlock()
{
Interlocked.Exchange(ref lockState, 0);
}
private void Init(int capacity)
{
T[] tx = new T[capacity];
for (int i = 0; i < capacity; i++)
{
tx[i] = unitFactory.Invoke();
}
units = tx;
this.capacity = capacity;
}
}
【问题讨论】:
-
Spinlocks vs Mutex stackoverflow.com/questions/5869825/… 并不是说这会解决您的问题,而是需要研究一下。
-
你在 ctor 之外使用 SetCapacity 吗?
-
@usr - 它的目的是,但我将派生它的私有版本以跳过锁定。请注意,有意缺少任何保护措施。
-
@usr - 我已将其删除以简化代码。
标签: c# .net multithreading deadlock