I have what I assume is a pretty common threading scenario:
I would use ThreadPool.
Before you start running your jobs, create a ManualResetEvent and an int counter. Add each job to the ThreadPool, incrementing the counter each time.
At the end of each job, decrement the counter and when it hits zero, call Set() on the event.
In your main thread, call WaitOne() to wait for all of the jobs to be completed.
Re: "somehow wait for them all to complete"
ManualResetEvent is your friend, before you start your big batch create one of these puppies, in your main thread wait on it, set it at the end of the background operation when the job is done.
Another option is to manually create the threads and do a foreach thread, thread.Join()
You could use this (I use this during testing)
private void Repeat(int times, int asyncThreads, Action action, Action done) {
if (asyncThreads > 0) {
var threads = new List<Thread>();
for (int i = 0; i < asyncThreads; i++) {
int iterations = times / asyncThreads;
if (i == 0) {
iterations += times % asyncThreads;
}
Thread thread = new Thread(new ThreadStart(() => Repeat(iterations, 0, action, null)));
thread.Start();
threads.Add(thread);
}
foreach (var thread in threads) {
thread.Join();
}
} else {
for (int i = 0; i < times; i++) {
action();
}
}
if (done != null) {
done();
}
}
Usage:
// Do something 100 times in 15 background threads, wait for them all to finish.
Repeat(100, 15, DoSomething, null)