如何检查信号量的状态

本文关键字:信号量 状态 检查 何检查 | 更新日期: 2023-09-27 18:08:03

我想检查Semaphore的状态,看看它是否有信号(所以如果t有信号,我可以释放它)。我该怎么做呢?

EDIT1:

我有两个线程,一个等待信号量,另一个释放Semaphore。问题是,第二个线程可以调用Release()几次,当第一个线程不等待它。因此,第二个线程应该检测它是否调用Release(),它是否产生任何错误(如果您试图释放一个没有人等待的信号量,它会产生一个错误)。我该怎么做呢?我知道我可以用一面旗帜来做这件事,但它很难看。有更好的办法吗?

如何检查信号量的状态

您可以通过调用WaitOne并传递超时值0作为参数来检查Semaphore是否有信号。这将导致WaitOne立即返回一个true或false值,指示信号量是否被发出信号。当然,这可能会改变信号量的状态,使其使用起来很麻烦。

这个技巧不能帮助您的另一个原因是,当至少有一个计数可用时,一个信号量被认为是有信号的。听起来您想知道信号量何时具有所有可用计数。Semaphore类没有那种确切的能力。您可以使用Release的返回值来推断计数是什么,但这会导致信号量改变其状态,当然,如果在调用之前信号量已经具有所有可用的计数,它仍然会抛出异常。

我们需要的是一个带有释放操作的信号量,该释放操作不会引发异常。这并不太难。如果计数可用,下面的TryRelease方法将返回true,如果信号量已经在maximumCount,则返回false。无论哪种方法都不会抛出异常。
public class Semaphore
{
    private int count = 0;
    private int limit = 0;
    private object locker = new object();
    public Semaphore(int initialCount, int maximumCount)
    {
        count = initialCount;
        limit = maximumCount;
    }
    public void Wait()
    {
        lock (locker)
        {
            while (count == 0) 
            {
                Monitor.Wait(locker);
            }
            count--;
        }
    }
    public bool TryRelease()
    {
        lock (locker)
        {
            if (count < limit)
            {
                count++;
                Monitor.PulseAll(locker);
                return true;
            }
            return false;
        }
    }
}

看起来您需要一个其他同步对象,因为Semaphore不提供这样的功能来检查它是否在特定时刻发出信号。

信号量允许使用WaitOne()/Release()方法自动触发等待信号状态的代码。

你可以看看新的.NET 4类SemaphoreSlim,它暴露了CurrentCount属性,也许你可以利用它。

CurrentCount
获取允许进入的线程数SemaphoreSlim。

EDIT:由于更新的问题而更新

作为一个快速的解决方案,你可以通过try/catch包装semaphore.Release()并处理SemaphoreFullException,它是否如你预期的那样工作?

使用SemaphoreSlim,你可以这样检查CurrentCount:

 int maxCount = 5;
 SemaphoreSlim slim = new SemaphoreSlim(5, maxCount);            
 if (slim.CurrentCount == maxCount)
 {
    // generate error
 }
 else
 {
   slim.Release();
 }

使用信令实现信号量的方法如下:在此之外查询状态是没有意义的,因为它不是线程安全的。

创建一个maxThreads插槽的实例,最初所有可用:

var threadLimit = new Semaphore(maxThreads, maxThreads);

使用以下命令等待(阻塞)一个空闲槽位(以防maxThreads已经被占用):

threadLimit.WaitOne();

使用以下命令释放槽位:

threadLimit.Release(1);

这里有一个完整的例子

知道信号量中的所有计数何时可用是有用的。我使用了以下逻辑/解决方案。我在这里分享是因为我还没有看到任何其他解决方案来解决这个问题。

//List to add a variable number of handles
private List<WaitHandle> waitHandles;
//Using a mutex to make sure that only one thread/process enters this section
using (new Mutex(....))
{
    waitHandles = new List<WaitHandle>();
    int x = [Maximum number of slots available in the semaphore];
    //In this for loop we spin a thread per each slot of the semaphore
    //The idea is to consume all the slots in this process 
    //not allowing anything else to enter the code protected by the semaphore
    for (int i = 0; i < x; i++)
    {
        Thread t = new Thread(new ParameterizedThreadStart(TWorker));
        ManualResetEvent mre = new ManualResetEvent(false);
        waitHandles.Add(mre);
        t.Start(mre);
    }
    WaitHandle.WaitAll(waitHandles.ToArray());
    [... do stuff here, all semaphore slots are blocked now ...]
    //Release all slots
    semaphore.Release(x);
}
private void TWorker(object sObject)
{
    ManualResetEvent mre = (ManualResetEvent)sObject;
    //This is an static Semaphore declared and instantiated somewhere else
    semaphore.WaitOne();
    mre.Set();
}