WaitHandle.WaitAny and Semaphore class

Thorarin picture Thorarin · Aug 19, 2009 · Viewed 7.8k times · Source

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.

Answer

Marcin Deptuła picture Marcin Deptuła · Aug 19, 2009

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.