Eksperymentuję z wątkami w języku C#, a w rezultacie utworzyłem następującą klasę. Starałem się unikać sytuacji związanych z wyścigami, a mimo to pojawił się impas.Zakleszczenie w klasie puli obiektów
Klasa wykorzystuje dwie różne blokady, jedną blokadę do prostej operacji, a dodatkowo blokadę Monitor
oczekiwania na wypadek, gdyby obiekt nie był gotowy. Pierwotnie używałem EventWaitHandle
, ale odkryłem, że warunki wyścigu były nieuniknione z powodu pierwszeństwa.
Należy pamiętać, że Monitor.Pulse
nie może poprzedzać Monitor.Wait
, więc co jeszcze może spowodować zakleszczenie? W przypadku, gdy 5 wątków używa klasy TestPool
o pojemności 4, impas zawsze występuje w SpinLock
w nieregularnym momencie.
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 http://stackoverflow.com/questions/5869825/when-should-one-use-a-spinlock-instead-of-mutex Nie mówiąc to rozwiąże problem, ale coś zbadać. – JNYRanger
Czy używasz SetCapacity poza ctor? – usr
@usr - Jest przeznaczony, ale ja wypiszę jego prywatną wersję, aby pominąć blokowanie. Należy pamiętać, że wszelkie zabezpieczenia są celowo nieobecne. – Tcqqp