在我花太长时间重新发明轮子之前,我想检查一下 .Net 中是否已经有一个类可以满足我的需求。
我想要的是有点像信号量(甚至可能像 CountdownEvent),但略有不同。
我有一个要求,我有不同数量的可用“资源”,并且我希望线程在可用资源为零时有效地等待。同时,另一个线程可以释放资源,这应该立即释放另一个等待线程。
这听起来很像信号量,但这并不是因为信号量(据我所知)在计算每个线程时将它们视为“资源”。
无论如何,这是我想要的第一个简单实现。它还没有处置、代码合同、错误处理、超时支持或取消支持,但它应该展示我想要的:
public sealed class ResourceCounter
{
/// <summary>Create with the specified number of resources initially available.</summary>
public ResourceCounter(int resourceCount)
{
_resourceCount = resourceCount;
if (_resourceCount > 0)
{
_resourceAvailable.Set();
}
}
/// <summary>Acquires a resource. Waits forever if necessary.</summary>
public void Acquire()
{
while (true)
{
_resourceAvailable.Wait();
lock (_lock)
{
if (_resourceCount > 0)
{
if (--_resourceCount == 0)
{
_resourceAvailable.Reset();
}
return;
}
}
}
}
/// <summary>Releases a resource.</summary>
public void Release()
{
lock (_lock)
{
++_resourceCount;
_resourceAvailable.Set();
}
}
private int _resourceCount;
private readonly object _lock = new object();
private readonly ManualResetEventSlim _resourceAvailable = new ManualResetEventSlim();
}
使用模式非常简单:
使用所需的初始资源计数(可以为零或更多)构造一个 ResourceCounter。
想要获取资源的线程调用 ResourceCounter.Acquire(),在资源可用且已获取之前不会返回。
想要释放资源的线程调用 ResourceCounter.Release(),它将释放资源并立即返回。
请注意,任何线程都可以释放资源;它不一定是获得资源的人。
我将它用作一些多线程管道代码的一部分,其中一个线程负责将工作项排队,几个线程正在处理工作项,另一个线程正在输出处理后的工作项。输出已处理工作项的线程必须对它们进行多路复用(因为处理线程可以以任何顺序输出已完成的项),并且我需要一种机制来阻止工作项在多路复用器等待延迟项时无休止地排队。
(有关这方面的一些背景信息,请参阅管道、多路复用和无界缓冲。)
无论如何,是否有任何可用的方法来做到这一点,还是我应该继续为此开发自己的课程?
[编辑]
如下所述,SemaphoreSlim 完全正确。我拒绝了它,因为我认为调用 Wait() 的线程必须是调用 Release() 的线程,但事实并非如此。这就是我在星期天编码所得到的……;)