【问题标题】:How to check the state of a semaphore如何检查信号量的状态
【发布时间】:2011-11-11 22:47:41
【问题描述】:

我想检查Semaphore 的状态以查看它是否已发出信号(因此,如果已发出 t 信号,我可以释放它)。我怎样才能做到这一点?

EDIT1:

我有两个线程,一个会等待信号量,另一个应该释放Semaphore。问题是当第一个线程没有等待它时,第二个线程可能会多次调用Release()。所以第二个线程应该检测到如果它调用Release(),它是否会产生任何错误(如果你试图释放一个信号量,如果没有人在等待它,它会产生一个错误)。我怎样才能做到这一点?我知道我可以使用一个标志来做到这一点,但它很难看。有没有更好的办法?

【问题讨论】:

  • 如何定义信号量的“信号”?在一般情况下,最多 M 个线程中的 N 个已进入其中。有信号吗?
  • Release 释放一个槽,它不需要线程处于等待模式。如果在没有人阻塞时释放它,则意味着下一个调用者将直接通过而不会阻塞。另一个线程应该只在需要释放时才调用释放。它不应该只是重复调用 release,否则不会有任何限制。

标签: c# .net multithreading semaphore


【解决方案1】:

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

这个技巧对您没有帮助的另一个原因是,当至少有一个计数可用时,信号量就会被发出信号。听起来您想知道信号量何时具有所有可用计数。 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;
        }
    }
}

【讨论】:

    【解决方案2】:

    看起来您需要其他同步对象,因为 Semaphore 不提供此类功能来检查它是否在特定时间发出信号。

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

    您可以查看新的.NET 4SemaphoreSlim,它公开了CurrentCount 属性,也许您可​​以利用它。

    当前计数
    获取允许进入的线程数 SemaphoreSlim。

    编辑:因更新问题而更新

    作为一种快速解决方案,您可以通过 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();
     }
    

    【讨论】:

      【解决方案3】:

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

      使用maxThreads 插槽创建一个实例,最初全部可用:

      var threadLimit = new Semaphore(maxThreads, maxThreads);
      

      使用以下命令等待(阻塞)备用插槽(以防 maxThreads 已被占用):

      threadLimit.WaitOne();
      

      使用以下命令释放插槽:

      threadLimit.Release(1);
      

      有一个完整的例子here

      【讨论】:

      • 确实,看看Win32 API 中的信号量函数列表:您可以创建、打开或释放,仅此而已。 (内核驱动程序可以获取此信息——Process Explorer 就是这样做的——但这超出了 .NET 问题的范围。)
      【解决方案4】:

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

      //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();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-25
        • 1970-01-01
        相关资源
        最近更新 更多