【问题标题】:Deadlock in object pool class对象池类中的死锁
【发布时间】: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


【解决方案1】:

修复它。我不得不将以下代码放在Monitor 锁之外。

Lock();

return units[--waitUnitPos];

【讨论】:

    猜你喜欢
    • 2010-09-10
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多