The correct way to implement ThreadPool.RegisterWaitForSingleObject

I am trying to use ThreadPool.RegisterWaitForSingleObject to add a timer to a set of threads. I create 9 threads and try to give each of them an equal chance to work, because at the moment it seems that there is a slight starvation if I just add them to the thread pool. I am also trying to execute a manual reset event, as I want all 9 threads to be completed before continuing.

What is the best way to ensure that every thread in the threadpool gets equal probability at startup, since the function I call has a loop, and it seems that every thread (or any of them starts first) gets stuck in it and the others don't get a chance to run.

resetEvents = new ManualResetEvent[table_seats]; //Spawn 9 threads for (int i = 0; i < table_seats; i++) { resetEvents[i] = new ManualResetEvent(false); //AutoResetEvent ev = new AutoResetEvent(false); RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(autoEvent, ObserveSeat, (object)i, 100, false); } //wait for threads to exit WaitHandle.WaitAll(resetEvents); 

However, it doesn't matter if I use resetEvents [], or ev does not seem to work properly. Can I implement this or am I (possibly) misunderstanding how they should work.

Thanks R.

+6
multithreading c # visual-studio-2008
source share
1 answer

I would not use RegisterWaitForSingleObject for this purpose. The samples I'm going to describe here require downloading Reactive Extensions since you are using .NET v3.5.

First, to wait for the use of all work items from ThreadPool to use the CountdownEvent class. This is much more elegant and scalable than using multiple instances of ManualResetEvent . In addition, the WaitHandle.WaitAll method WaitHandle.WaitAll limited to 64 handles.

 var finished = new CountdownEvent(1); for (int i = 0; i < table_seats; i++) { finished.AddCount(); ThreadPool.QueueUserWorkItem(ObserveSeat); (state) => { try { ObserveSeat(state); } finally { finished.Signal(); } }, i); } finished.Signal(); finished.Wait(); 

Secondly, you can try calling Thread.Sleep(0) after several iterations of the loop to force the context switch so that the current thread gives another. If you want a much more complex coordination strategy, use the Barrier class. Add another parameter to your ObserveSeat function, which accepts this synchronization mechanism. You can provide it by capturing it in a lambda expression in the code above.

 public void ObserveSeat(object state, Barrier barrier) { barrier.AddParticipant(); try { for (int i = 0; i < NUM_ITERATIONS; i++) { if (i % AMOUNT == 0) { // Let the other threads know we are done with this phase and wait // for them to catch up. barrier.SignalAndWait(); } // Perform your work here. } } finally { barrier.RemoveParticipant(); } } 

Note that while this approach will undoubtedly prevent the problem of starvation, it can limit the bandwidth of the threads. Calling SignalAndWait too much can cause a lot of unnecessary context switching, but calling it too little can cause a lot of unnecessary waiting. You will probably need to tune AMOUNT to get the optimal balance of bandwidth and hunger. I suspect there might be an easy way to do the setup dynamically.

+4
source share

All Articles