What's the proper way to wait on a Semaphore?

John Smith picture John Smith · Jan 25, 2013 · Viewed 8.9k times · Source

I thought that the following code would let all the 10 threads run, two at a time, and then print "done" after Release() is called 10 times. But that's not what happened:

        int count = 0;

        Semaphore s = new Semaphore(2, 2);
        for (int x = 0; x < 10; x++)
        {
            Thread t = new Thread(new ThreadStart(delegate()
            {
                s.WaitOne();
                Thread.Sleep(1000);
                Interlocked.Increment(ref count);                       
                s.Release();
            }));
            t.Start(x);
        }

        WaitHandle.WaitAll(new WaitHandle[] { s });
        Console.WriteLine("done: {0}", count);

output:

done: 6

If the only way to implement the functionality I'm looking for is to pass an EventWaitHandle to each thread and then do a WaitAll() on an array of those EventWaitHandles, then what's the meaning of doing a WaitAll() on an array of only a semaphore? In other words, when does the waiting thread unblock?

Answer

Nikolay Khil picture Nikolay Khil · Jan 25, 2013

WaitHandle.WaitAll just waits until all the handlers are in signalled state.

So when you call WaitHandle.WaitAll on one WaitHandle it works the same as you call s.WaitOne()

You can use, for example, the following code to wait for all the started threads, but allow two threads to run in parallel:

int count = 0;

Semaphore s = new Semaphore(2, 2);
AutoResetEvent[] waitHandles = new AutoResetEvent[10];
for (int x = 0; x < 10; x++)
    waitHandles[x] = new AutoResetEvent(false);

for (int x = 0; x < 10; x++)
{
    Thread t = new Thread(threadNumber =>
        {
            s.WaitOne();
            Thread.Sleep(1000);
            Interlocked.Increment(ref count);
            waitHandles[(int)threadNumber].Set();
            s.Release();
        });
    t.Start(x);
}

WaitHandle.WaitAll(waitHandles);
Console.WriteLine("done: {0}", count);