I have a scenario when I start 3..10 threads with ThreadPool. Each thread does its job and returns to the ThreadPool. What are possible options to be notified in main thread
Decrementing a variable (between threads) is a little bit risky unless done with Interlocked.Decrement
, but that approach should be fine if you have the last thread (i.e. when it gets to zero) raise an event. Note that it would have to be in a "finally" block to avoid losing it in the case of exceptions (plus you don't want to kill the process).
In "Parallel Extensions" (or with .NET 4.0), you might also look at the Parallel.ForEach
options here... that might be another way of getting everything done as a block. Without having to watch them all manually.
Try this: https://bitbucket.org/nevdelap/poolguard
using (var poolGuard = new PoolGuard())
{
for (int i = 0; i < ...
{
ThreadPool.QueueUserWorkItem(ChildThread, poolGuard);
}
// Do stuff.
poolGuard.WaitOne();
// Do stuff that required the child threads to have ended.
void ChildThread(object state)
{
var poolGuard = state as PoolGuard;
if (poolGuard.TryEnter())
{
try
{
// Do stuff.
}
finally
{
poolGuard.Exit();
}
}
}
Multiple PoolGuards can be used in different ways to track when threads have ended, and handles threads that haven't started when the pool is already closed.
Couldn't you give each thread a distinct ManualResetEvent and have each set the event when done. Then, in the main thread you can wait on all the events passed in.
If its not more than 64 Threads to wait on, you can use the WaitHandle.WaitAll method like this:
List<WaitHandle> events = new List<WaitHandle>();
for (int i = 0; i < 64; i++)
{
ManualResetEvent mre = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(
delegate(object o)
{
Thread.Sleep(TimeSpan.FromMinutes(1));
((ManualResetEvent)o).Set();
},mre);
events.Add(mre);
}
WaitHandle.WaitAll(events.ToArray());
The execution will wait till all ManualResetEvents are set, alternatively, you may use WaitAny method.
The WaitAny and WaitAll methods will block the execution, but you can simply use the list, or a dictionary of ManualResetEvents linked to the task that is spawn for later determining if the thread is done though.
There's isn't a built-in way to do this at the moment - I find it one of the biggest pains about using pool threads.
As Marc says, this is the kind of stuff which is being fixed in Parallel Extensions / .NET 4.0.
Marc's solution is best if you just want to know when all the jobs are finished, and don't need finer info than that (as seems to be your case).
If you wanted some thread to spawn jobs, and some other thread to to receive the notifications, you could use WaitHandle. The code is much longer.
int length = 10;
ManualResetEvent[] waits = new ManualResetEvent[length];
for ( int i = 0; i < length; i++ ) {
waits[i] = new ManualResetEvent( false );
ThreadPool.QueueUserWorkItem( (obj) => {
try {
} finally {
waits[i].Set();
}
} );
}
for ( int i = 0; i < length; i++ ) {
if ( !waits[i].WaitOne() )
break;
}
The WaitOne method, as written, always returns true, but I have written it like that to make you remember that some overloads take a Timeout as an argument.