February 20, 2019
Hot Topics:

Batched Execution Using the .NET Thread Pool

  • January 7, 2005
  • By Nick Wienholt
  • Send Email »
  • More Articles »

To highlight the general usefulness of this pattern, a helper method that implements the pattern is shown below. The method takes a delegate array of tasks that should be executed in a batch and have no dependencies on each other, and returns when all the tasks have completed, at which time other tasks can be scheduled.

//Used to pass state to the thread pool callback.
//Holds original delegate, its state, and the event.
__gc class StateHolder{
      StateHolder(WaitCallback* wc, Object* orginalState,
                  ManualResetEvent* event){
         OriginalCallback = wc;
         OriginalState = orginalState;
         Event = event;

   Object* OriginalState;
   ManualResetEvent* Event;
   WaitCallback* OriginalCallback;;

__sealed __gc public class ThreadPoolHelper{
   //used to run task then set event
   static void TaskRunner(Object* stateInfo){
      StateHolder __gc* state =
         __try_cast<StateHolder __gc*>(stateInfo);

   static bool QueueMultipleUserWorkItem(
      WaitCallback __gc* callbacks __gc[],
                         Object __gc* states __gc[]){
      //check params
      if (callbacks == 0)
      throw new ArgumentNullException(S"callbacks");

   if (states != 0 && callbacks->Length != states->Length)
   throw new ArgumentException(
      S"states must be null or the same length as callbacks",

   //queue each callback
   ArrayList __gc* handles = new ArrayList();
   for(int ix = 0; ix < callbacks->Length; ++ix){
      ManualResetEvent* event = new ManualResetEvent(false);
      StateHolder __gc* state = new StateHolder(callbacks[ix],
                                states != 0? states[ix]: 0, event);
      if (!ThreadPool::QueueUserWorkItem
      (new WaitCallback(0, ThreadPoolHelper::TaskRunner), state))
      return false;

   //wait for all handles
   WaitHandle::WaitAll(__try_cast<WaitHandle __gc*[]>

   return true;

//example use
__gc class HelperExamples{
   static void Task1(Object* stateInfo){

static void Task2(Object* stateInfo){

static void Task3(Object* stateInfo){

static void Task4(Object* stateInfo){

int _tmain()
   Console::WriteLine(S"Batched thread pool use using helper");
   Console::WriteLine(S"Batching tasks 1 and 2");
   WaitCallback __gc* callbacks __gc[]  =
      new WaitCallback __gc* __gc[2];
   callbacks[0] = new WaitCallback(0, HelperExamples::Task1);
   callbacks[1] = new WaitCallback(0, HelperExamples::Task2);
   ThreadPoolHelper::QueueMultipleUserWorkItem(callbacks, 0);

   Console::WriteLine(S"Tasks 1 and 2 done. Batching 3 and 4");
   callbacks[0] = new WaitCallback(0, HelperExamples::Task3);
   callbacks[1] = new WaitCallback(0, HelperExamples::Task4);
   ThreadPoolHelper::QueueMultipleUserWorkItem(callbacks, 0);

   Console::WriteLine(S"All tasks done");

   return 0;
If you need to execute a batch asynchronously, you can define a delegate with the same signature as the batch runner method and use this to complete an asynchronous call (which uses the ThreadPool under the hood).

Download the Code

To download the accompanying source code for the demo, click here.

About the Author

Nick Wienholt is an independent Windows and .NET consultant based in Sydney, Australia. He is the author of Maximizing .NET Performance from Apress, and specializes in system-level software architecture and development with a particular focus on performance, security, interoperability, and debugging. Nick can be reached at NickW@dotnetperformance.com.

Page 2 of 2

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date