对象池类中的死锁

Tcq*_*qqp 5 .net c# multithreading deadlock

我在C#中尝试使用线程,结果创建了以下类.我试图避免任何竞争条件,但使用时会出现死锁.

该类使用两个不同的锁,一个用于简单操作的自旋锁,另外还有一个Monitor锁以在没有对象准备好的情况下等待.我最初使用EventWaitHandle,但发现种族条件由于WaitOne/ Set优先而不可避免.

注意,Monitor.Pulse不能先于Monitor.Wait,否则还有什么可能导致死锁?在5个线程使用TestPool容量为4 的类的情况下,死锁总是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Tcq*_*qqp 0

修复。我必须将以下代码放在Monitor锁外面。

Lock();

return units[--waitUnitPos];
Run Code Online (Sandbox Code Playgroud)