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?

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. Commented Jan 25, 2013 at 20:13
  • 2
    Just FYI, you may want to consider using Interlocked.Increment instead of volatile: stackoverflow.com/questions/154551/… Commented Jan 25, 2013 at 20:15
  • 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"); Commented Jan 25, 2013 at 20:33
  • 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. Commented Jan 25, 2013 at 20:41
  • If you want to wait for all threads to finish, keep the Thread objects around and call Join on each one in turn. Commented Jan 25, 2013 at 21:01

2 Answers 2

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); 
Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.