If you have a reference to the threads you're waiting to finish you can call the .Join() method on those references. This will cause your main thread to wait until the threads you've called .Join() on to finish before it proceeds.
Alternatively you could create an array of ManualResetEvents that both your main thread and child thread have access to. Each child thread has a reference to one event in the array the main thread has access to. Call WaitHandle.WaitAll(array of events) on event array at end of main thread, and call .Set() on event at end of each child thread to signal main thread child threads are done.
Before main thread exits, below will block until all child threads calls .Set() on their events:
WaitHandle.WaitAll(array of events);