对象池类中的死锁

时间:2015-03-03 19:30:46

标签: c# .net multithreading deadlock

我正在尝试使用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;
    }
}

1 个答案:

答案 0 :(得分:0)

修正了它。我必须将以下代码放在Monitor锁定之外。

Lock();

return units[--waitUnitPos];
相关问题