代码之家  ›  专栏  ›  技术社区  ›  Thorarin

waithandle.waitany和信号量类

  •  6
  • Thorarin  · 技术社区  · 15 年前

    编辑: 我想请求暂时的精神错乱,即使问这个问题,但它在当时是有意义的(见下面的编辑2)。

    对于.NET 3.5项目,我有两种类型的资源( R1号 R2 )我需要检查的可用性。每种资源类型可以在任何时候有(比如)10个实例。

    当任何一种资源可用时,我的工作线程都需要唤醒(线程数量可变)。在早期的实现中,只有一种资源类型,我使用信号量检查其可用性。

    现在我需要等待两个单独的信号灯( S1 S2 )跟踪资源的可用性。

    WaitHandle[] waitHandles = new WaitHandle[] { s1, s2 };
    int signalledHandle = WaitHandle.WaitAny(waitHandles);
    
    switch (signalledHandle)
    {
        case 0:
            // Do stuff
            s1.Release();
        case 1:
            // Do stuff
            s2.Release();
    }
    

    不过,这有一个问题。从上的msdn文档 WaitAny :

    如果多个对象 在呼叫过程中发出信号,返回 值是的数组索引 最小的信号对象 所有信号的索引值 物体。

    这表明我可能在调用后将两个信号量计数都减少了1。 侍从 .因为 signalledHandle 将指示s1已发出信号,我将开始使用资源 R1号 ,完成后释放。但是,因为我不知道 S2 是否已发出信号,此资源的可用性计数现在可能已关闭。如果这发生了10次,我的信号量将永远是“空的”和资源 R2 将不再使用。

    最好的方法是什么?我应该从使用两个信号灯切换到简单的计数器和一个autoresetEvent,当其中一个计数器发生变化时发出信号吗?我是否错过了一些更优雅的方法?

    编辑1:
    根据Ravadre的说法,在 侍从 . 稍微修改一下他的示例似乎证实了这一点,但是有没有人可以向我指出一些官方文档中指定了这一点?

    编辑2:
    我在回家的路上想到了这个。直到那时我才意识到这一定是真的 侍从 任何用途。这个问题不局限于信号量,而是关于任何类型的同步对象,使得 侍从 几乎没用。

    2 回复  |  直到 7 年前
        1
  •  5
  •   Marcin Deptuła    15 年前

    如果我正确理解您的问题,我认为您的解决方案是完全正确的,您只是过度解释了msdn的报价。打电话时 WaitHandle.WaitAny() 您将获得最低的索引,但您将只锁定一个waithandle(本例中为信号量),请检查此示例代码:

    
    Semaphore s1 = new Semaphore(1, 2);
    Semaphore s2 = new Semaphore(1, 2);
    
    WaitHandle[] handles = new WaitHandle[] { s1, s2 };
    
    int x = WaitHandle.WaitAny(handles);
    
    int prevS1 = s1.Release();
    int prevS2 = s2.Release();
    

    在这种情况下, prevS1 将等于0,因为信号量 s1 “被等待”,所以它的计数器被减少到0,而 prevS2 将等于1,因为它的状态自实例化后未更改( Release() 方法在释放前返回计数器,因此返回1表示“它是1,现在是2”)。

    您可能需要查看的另一个资源: http://www.albahari.com/threading/part2.aspx#_Wait_Handles . 虽然它不是一个“官方”消息来源,但我认为没有理由认为它不可靠。

        2
  •  1
  •   J. Lennon    7 年前

    为了你的目的,打电话时 WaitHandle.WaitAny() 方法结果无关紧要。重要的是有一个waithandle被发出信号,所以您需要再次尝试获取锁/同步。

    void Main() {
     var semaphoreOne = new SemaphoreSlim(0, 1);
     var semaphoreTwo = new SemaphoreSlim(0, 1);
    
     ReleaseSemaphoreAfterWhile(semaphoreOne);
    
     bool firstAccepted;
     bool secondAccepted = false;
     while ((firstAccepted = semaphoreOne.Wait(0)) == false &&
      (secondAccepted = semaphoreTwo.Wait(0)) == false) {
      var waitHandles = new [] {
       semaphoreOne.AvailableWaitHandle, semaphoreTwo.AvailableWaitHandle
      };
      WaitHandle.WaitAny(waitHandles);
      Console.WriteLine("SemaphoreOne Before Lock = " + semaphoreOne.CurrentCount);
      Console.WriteLine("SemaphoreTwo Before Lock = " + semaphoreTwo.CurrentCount);
     }
    
     if (firstAccepted) {
      Console.WriteLine("semaphore 1 was locked");
     } else if (secondAccepted) {
      Console.WriteLine("semaphore 2 was locked");
     } else {
      throw new InvalidOperationException("no semaphores were signaled");
     }
    }
    
    Random rd = new Random();
    public void ReleaseSemaphoreAfterWhile(SemaphoreSlim semaphore) {
    var sleepWork =(int)rd.Next(100, 1000);
     ThreadPool.QueueUserWorkItem(t => {
      Thread.Sleep(10000 + sleepWork);
      semaphore.Release();
     });
    }
    

    对于具有相同想法/逻辑的其他实现来说,还有空间,但是以这种方式使用while循环可以确保只获取一个信号量,如果没有空间,它将锁定线程,直到任何waithandle收到信号-考虑 SemaphoreSlim 实例 .Release() 方法。

    不幸的是(正如评论中指出的那样),它们对Web中的线程同步有些误解,但是上面的代码应该可以帮助您解决问题。

    推荐文章