WaitHandle.WaitAny 和 Semaphore 类

发布于 2024-08-01 20:46:31 字数 1395 浏览 6 评论 0原文

编辑:我想为自己提出这个问题辩护,但当时这是有道理的(见下面的编辑2)。

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

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

现在我需要等待两个独立的信号量(S1S2)来跟踪资源的可用性。

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

然而,这有一个问题。 来自 WaitAny 的 MSDN 文档:

如果多个对象变成 在通话过程中发出信号,返回 value 是数组索引 具有最小信号的对象 所有信号的索引值 对象。

这表明我在调用 WaitAny 后可能将两个信号量计数都减少了 1。 因为 signalledHandle 将指示 s1 已发出信号,所以我将开始使用资源 R1,并在完成后释放它。 但是,由于我不知道 S2 是否已发出信号,因此该资源的可用性计数现在可能已关闭。 如果这种情况发生 10 次,我的信号量将永久“空”,并且资源 R2 将不再被使用。

处理这个问题的最佳方法是什么? 我是否应该从使用两个信号量切换为简单计数器,并在任一计数器发生更改时使用 AutoResetEvent 发出信号? 我错过了一些更优雅的方法吗?

编辑1:
根据 Ravadre 的说法,在 WaitAny 之后实际上只会更改其中一个信号量。 稍微修改他的示例似乎证实了这一点,但是有没有人可以向我指出一些指定这一点的官方文档?

编辑2:
我在回家的路上思考着这个问题。 直到那时我才意识到,WaitAny 必须是这样才有用。 这个问题不仅限于信号量,而是几乎任何类型的同步对象,使得 WaitAny 实际上毫无用处。

Edit: I'd like to plead temporary insanity for even asking this question, but it made sense at the time (see edit 2 below).

For a .NET 3.5 project, I have two types of resources (R1 and R2) that I need to check the availability of. Each resource type can have (say) 10 instances at any time.

When one of either types of resources becomes available, my worker thread needs to wake up (there is a variable number of threads). In an earlier implementation, there was only one resource type, for which I used a Semaphore to check availability.

Now I need to wait on two separate Semaphores (S1 and S2) that track availability of the resources.

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

There is one problem with this however. From the MSDN documentation on WaitAny:

If more than one object becomes
signaled during the call, the return
value is the array index of the
signaled object with the smallest
index value of all the signaled
objects.

This suggests that it's possible that I decreased both my Semaphore counts by 1 after calling WaitAny. Because signalledHandle will indicate that s1 was signalled, I will start using resource R1, and release it when I'm done. However, since I do not know if S2 was signalled or not, the availability count on this resource might now be off. If this happens 10 times, my Semaphore will be permanently 'empty' and resource R2 will not be used at all anymore.

What is the best way to deal with this? Should I switch from using two semaphores to simple counters and an AutoResetEvent to signal when either counter is changed? Am I missing some more elegant approach?

Edit 1:
According to Ravadre, only one of the Semaphores will actually be altered after WaitAny. Slightly modifying his example seems to confirm this, but is there anyone that can point me to some piece of official documentation that specifies this?

Edit 2:
I was thinking about this on my way home. Only then I realized that this must be true for WaitAny to be of any use. This problem would not be restricted to Semaphores, but just about any type of synchronization object, making WaitAny practically useless.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

从来不烧饼 2024-08-08 20:46:31

如果我正确理解你的问题,我认为你的解决方案完全没问题,你只是过度解释了 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< /code> 将等于 0,因为信号量 s1 “已等待”,因此它的计数器已减少为 0,而 prevS2 将等于 1,因为它的状态自实例化以来一直没有改变(Release() 方法在释放之前返回计数器,因此返回 1 意味着“以前是 1,现在是 2”)。

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

If I understand your problem correctly, I think that your solution is perfectly ok, and you are just over interpreting the msdn quote. When calling WaitHandle.WaitAny() you will get the lowest index, but you will lock on only one waitHandle (semaphore in this case), check this sample code:


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

In this scenario, prevS1 will be equal to 0, because semaphore s1 "was waited on", so it's counter has been reduced to 0, whereas prevS2 will be equal to 1, because it's state hasn't changed since it's instantiation (Release() method returns the counter before releasing, so returning 1 means "it was 1, now it's 2").

Another resource that you might want to look at : http://www.albahari.com/threading/part2.aspx#_Wait_Handles. Although it's not an "official " source, I think there's no reason to find it not reliable.

梦冥 2024-08-08 20:46:31

出于您的目的,调用 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() 方法。

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

For your purpose, when calling WaitHandle.WaitAny() method the result doesn't matter. What matters is one WaitHandle was signaled, so you need to try to acquire lock/synchronization again.

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

There are room for other implementations with the same idea/logic, but using while loop in that way you guaranteed that only one semaphore is going to get acquired, and if there's no room, it locks the thread until any of the WaitHandle gets signaled - considering SemaphoreSlim instance .Release() method.

Unfortunately (as pointed in the comments) they're some misunderstanding about thread synchronization in the web, but that code above should help you to solve your problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文