信号量由于停止在自己的线程上运行的代码而陷入僵局

时间:2019-01-31 16:53:34

标签: c# .net multithreading

为了能够运行在多线程环境中无法很好扩展的库,我创建了一个对象池来自动创建有限数量的实例,以增加总吞吐量。我正在使用SemaphoreSlim来锁定/解锁对基础实例的访问。

为完整性起见,发现here on MSDN源自ObjectPool<T>

/// <summary>
/// An object pool with a fixed amount of objects.
/// </summary>
/// <typeparam name="T">The type to have in this pool</typeparam>
public class FixedObjectPool<T> : ObjectPool<T>
    where T : class
{
    private int _maxObjectCount;
    private SemaphoreSlim _objectAvailable;

    public FixedObjectPool(Func<T> objectGenerator, int maxObjectCount) : base(objectGenerator)
    {
        _maxObjectCount = maxObjectCount;
        _objectAvailable = new SemaphoreSlim(0, maxObjectCount);
    }

    public override T GetObject()
    {
        _objectAvailable.Wait(10);
        return base.GetObject();
    }

    public override void PutObject(T item)
    {
        base.PutObject(item);
        _objectAvailable.Release();
    }
}

该类每秒应该被调用数千次。除非指定了超时时间(当前为10ms),否则对GetObject方法的调用似乎会死锁。

我的2美分是调用代码在被锁定的同一线程上运行,因此信号量没有释放。

我如何通过此类的设计来确保方法调用是在其自己的线程上分派的,目的是通过停止在此方法之外运行的代码来确保信号量等待不会死锁?

编辑:根据要求,当前正在使用该池,如下所示;

Parallel.For(0, 10000, (i) => {
    var instance = pool.GetObject();
    // Do something with said instance
    pool.PutObject(instance);
});

2 个答案:

答案 0 :(得分:2)

您可以使用初始计数为零的信号量进行初始化:

_objectAvailable = new SemaphoreSlim(0, maxObjectCount);

这意味着所有谁通话Wait将阻止不管什么线程他们都在。

答案 1 :(得分:1)

问题是您将信号灯的初始计数设置为零:

_objectAvailable = new SemaphoreSlim(0, maxObjectCount);

这意味着当您呼叫GetObject时将没有任何可用的信号,并且信号灯将超时。

您的池开始时是空的,因此您需要先在其中添加一些内容。这将增加信号量,也意味着您有收获!

您提到该类在多线程环境中无法很好地扩展,而且我不确定您的解决方案是否可以使用。您基本上是在等待信号量,然后通过调用基类来创建对象。正确使用信号量后,您将获得多个线程,这些线程可以同时调用基类,这意味着基类必须是线程安全的。