Wednesday, November 08, 2006

Waiting for threads

I know I'm stating the obvious, but I've seen this in one too many occasions - I had to act...

Sometimes you have an application that must do some job with multiple threads, and once they all complete, you can proceed with a concluding action. The straight-forward code would be as such:


public class StraightForward
{
private int numberOfThreads;
private Thread[] threads;

public StraightForward(int numberOfThreads)
{
this.numberOfThreads = numberOfThreads;
}

public void DoWork()
{
// Launch the threads to do the work
threads = new Thread[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++)
{
threads[i] =
new Thread(new ThreadStart(doWorkImpl));
threads[i].Start();
}

// Wait for all the threads to complete
for (int i = 0; i < numberOfThreads; i++)
{
threads[i].Join();
}

// Do the concluding work
onWorkCompleted();
}

private void doWorkImpl()
{
// Do something here
}

private void onWorkCompleted()
{
// Do some concluding work here
}
}

Using a threadpool instead of launching the threads yourself is an option, but then I would strongly recommend Ami Bar's SmartThreadPool. Also, to be able to "join" threads from the .NET's threadpool you may want to use Stephen Toub's ThreadPoolWait. Anyway, for our discussion we will assume you have very good reasons for not using a threadpool at all.

The problem with the code above is that it requires an additional thread for "administrative" purposes only. That is, either the calling thread (like the code above) or an additional thread (to run DoWork asynchronously) must block there, waiting for all the others to complete their job and then execute onWorkCompleted.

The solution is very simple - keep track of the number of threads still running using an integer (that is handled only through the Interlocked class). The last thread to complete will know it's the last one and will be responsible to execute the concluding onWorkCompleted method. Here is a sample:


public class NoRedundantThread
{
private int numberOfThreads;
private Thread[] threads;
private int runningThreads;

public NoRedundantThread(int numberOfThreads)
{
this.numberOfThreads = numberOfThreads;
}

public void DoWork()
{
// Launch the threads to do the work
threads = new Thread[numberOfThreads];
runningThreads = numberOfThreads;
for (int i = 0; i < numberOfThreads; i++)
{
threads[i] =
new Thread(new ThreadStart(doWorkImpl));
threads[i].Start();
}
}

private void doWorkImpl()
{
// Do something here
// ...

// If we're the last thread - run the concluding method
if (0 == Interlocked.Decrement(ref runningThreads))
{
onWorkCompleted();
}
}

private void onWorkCompleted()
{
// Do some concluding work here
}
}

Now the code is asynchronous, without the need for a thread that just sits there, waiting for the others to do the work :-)

No comments: