Another option to spawning the threads yourself is to use ThreadPool.QueueUserWorkItem.

You can create a background thread, and this thread controls sending 'jobs' the the QueueUserWorkItem method. This method takes a delegate so when you push the job onto the queue, you create and pass in an event (and store each event into an array). The thread proc function sets this event when it's finished with the work and. After pushing all the 'job's onto the QueueUserWorkItem, the background thread justs waits on the events in the array.