Class WaitHandle.WaitAny and Semaphore

Edit: I would like to ask for temporary insanity for even asking this question, but that made sense at the time (see board 2 below).

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

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

Now I need to wait on two separate Semaphores (S1 and S2) that track the availability of 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 a signal during a call, the return value is the index of the array of the signal object with the lowest index value for all signal objects.

This suggests that perhaps I reduced both accounts of Semaphore by 1 after calling WaitAny . Since the signalledHandle will indicate that s1 has been signaled, I will start using the R1 resource and release it when done. However, since I do not know if S2 was signaled or not, the amount of available resources on this resource can now be disabled. If this happens 10 times, my Semaphore will be “empty” forever, and resource R2 will no longer be used.

What is the best way to handle this? Should I switch from using two semaphores to simple counters and AutoResetEvent to signal when the counter changes? Is there an even more elegant approach?

Change 1:
According to Ravadra, only one of the Semaphores will actually be changed after WaitAny . Changing his example a bit seems to confirm this, but is there anyone who can point me to some kind of official documentation that points this out?

Edit 2:
I thought about it on the way home. Only then did I realize that this should be true for WaitAny . This problem will not be limited to semaphores, but only with any type of synchronization object, making WaitAny practically useless.

+6
multithreading c # waithandle
source share
2 answers

If I understand your problem correctly, I think your solution is ok and you are simply interpreting the msdn quote. When you call WaitHandle.WaitAny() you will get the lowest index, but you will block only one waitHandle (semaphore in this case), check this code example:

 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 case, prevS1 will be 0 because the semaphore s1 “expected to”, so the counter was reduced to 0, while prevS2 would be 1, because it hasn’t changed since it was created (the Release() method returns the counter before release, so returning 1 means "it was 1, now it's 2").

Another resource you can see: http://www.albahari.com/threading/part2.aspx#_Wait_Handles . Although this is not an “official” source, I think that there is no reason to consider it unreliable.

+5
source share

For your purpose, when you call WaitHandle.WaitAny() result does not matter. The important thing is that the WaitHandle signal has been signaled, so you need to try to get the lock / sync 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 places for other implementations with the same idea / logic, but using a while loop this way, you guarantee that only one semaphore is going to get, and if there is no space, it blocks the thread until any of WaitHandle receives a signal - considering the SemaphoreSlim instance .Release() method.

Unfortunately (as indicated in the comments), they represent some misunderstanding about the synchronization of streams on the Internet, but the above code will help you solve your problem.

0
source share

All Articles