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

2

9

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?

Female answered 25/1, 2013 at 20:9 Comment(5)
It's very possible you are seeing a localized race condition; Console.WriteLine has it's own locks around it and the order in that the content is output is not guaranteed to be the order in which you call it. You might want to write tick values or use a logging framework that handles this case better. The output doesn't show that two threads aren't running at the same time two-at-a-time.Wingard
Just FYI, you may want to consider using Interlocked.Increment instead of volatile: #155051Stanleystanly
casperOne, I'm not sure if that's correct, because I experience the same problem even if I replace the last line with System.Diagnostics.Debug.WriteLine("done");Female
Im not really sure what the point of this example is - You're starting 10 threads anyways. If you want to run two threads at a time, why not just open only two threads and devide the work evenly.Tissue
If you want to wait for all threads to finish, keep the Thread objects around and call Join on each one in turn.Akan
V
1

WaitHandle.WaitAll(new WaitHandle[] { s }); waits just like s.WaitOne();. It enters at the first opportunity. You seem to expect this call to wait for all other semaphore operations but there is no way the operating system can tell the difference. This command might well be the first that is granted access to the semaphore.

I think what you need is the Barrier class. It is made for fork-join-style parallelism.

Vibrations answered 25/1, 2013 at 20:38 Comment(0)
B
6

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);
Ballata answered 25/1, 2013 at 20:38 Comment(0)
V
1

WaitHandle.WaitAll(new WaitHandle[] { s }); waits just like s.WaitOne();. It enters at the first opportunity. You seem to expect this call to wait for all other semaphore operations but there is no way the operating system can tell the difference. This command might well be the first that is granted access to the semaphore.

I think what you need is the Barrier class. It is made for fork-join-style parallelism.

Vibrations answered 25/1, 2013 at 20:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.