This is all happening in a windows service.
I have a Queue (actually a ConcurrentQueue) holding items waiting to be processed
Use BlockingCollection instead of ConcurrentQueue, then you can start any number of consumer threads and use Take method of the BlockingCollection. if the collection is empty, the Take method will automatically block in the caller thread waiting for items to be added, otherwise the threads will consume all the queue items in parallel. However as your question mentioned out the use of TPL it turns out that Parallel.ForEach have some issues when using with BlockingCollection check this post for more details. so you have to manage creation of your consumer threads your self. new Thread(/*consumer method*/) or new Task()...