c# - 对象池类中的死锁

标签 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;
    }
}

最佳答案

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

Lock();

return units[--waitUnitPos];

关于c# - 对象池类中的死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28840326/

相关文章:

c# - Windows窗体组织代码

c# - 是否可以创建构造函数模板?

c# - ObservableCollection<T> 存在于 'System.Windows' 和 'System'

javascript - 连续传递样式和并发

c# - 使用 NuGet 安装 json.net 后,如何在类里面使用它?

c# - Facebook .NET SDK : How to authenticate with ASP. NET MVC 2

.net - 在 HttpClient 上设置代理时出现问题

c# - 获取一个类在 C# 中继承和实现的所有类型和接口(interface)

c# - 长时间运行的任务阻塞了 UI

python - uWSGI Workers 写入同一个 JSON 文件