views:

1141

answers:

7

Hello, comrades.

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 when all background threads have finished?

Currently I'm using a homegrown method with incrementing a variable for each of created threads and decrementing it when a background thread is about to finish. This works just fine, but I was curious if there are better options.

Thanks, Valve.

+6  A: 

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.

Marc Gravell
Hi Marc.I'm doing it in a thread safe manner, with either Interlocked or lock keyword. It works fine and in a non-blocking manner. I was wondering if there are built-in primitives for doing that.Thanks.
Valentin Vasiliev
+1  A: 

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.

Will Dean
+1  A: 

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.

Sean
A: 

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.

DonkeyMaster
A: 

Hi Sean and Ruijoel. Will the WaitOne method be a blocking one? My goal is to spawn these threads and move on without being blocked by 'waiting for notifications'.

Please let me know what you think.

Valve.

Valentin Vasiliev
You'll want to use the WaitHandle.WaitAll method. This will allow you to pass in all the handles and a timeout. If you pass in a timeout of 0 it will check if all are complete.
Sean
+1  A: 

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.

BeowulfOF
A: 

What about using Semaphore, and set a limit to it as much as your thread pool. Have a method to fetch a Semaphore, to be called when you start your thread, release it when your thread end and raise an event if you've taken up all the Semaphore.

faulty