Not logged in - Login

Execute multiple tasks with maximum simultaneous thread

This example illustrates how to execute a task on multiple threads, but also specifying a maximum number of threads that can be running at the same time.

Main method that starts and runs the tasks

public void DoTest()
{
   // set some constants on how we want this to run
   const int totalTasksToRun = 100;   // total number of tasks we want to run
   const int maxTasksToRunSimultaneously = 50;   // maximum number of tasks to be running at the same time.


   Console.WriteLine("Submitting Tasks");

   // create a List to hold all our worker tasks
   List<System.Threading.Tasks.Task<MyOutput>> workerTasks = new List<System.Threading.Tasks.Task<MyOutput>>();


   // create an inline function to be called after a task is complete
   Action<MyOutput> fnDivideByThree = (threeTask) =>
   {
      Console.WriteLine(string.Format("Task {0} is divisible by 3", threeTask.TaskNum));
   };


   // loop a specified number of times
   for (int taskNum = 1; taskNum <= totalTasksToRun; taskNum++)
   {
      // need a local variable so the right number gets detected in the start of the task
      int localTaskNum = taskNum;

      // create a task and run it in a new thread
      System.Threading.Tasks.Task<MyOutput> returnTask = System.Threading.Tasks.Task.Run(() => this.Task1(localTaskNum));

      // Make some determination on which method is to be called after the task completes.
      // In this example, using even/odd state.
      System.Threading.Tasks.Task<MyOutput> continueTask1 = null;
      if (taskNum % 2 == 0)
      {
         // we have an even task
         continueTask1 = returnTask.ContinueWith<MyOutput>(ct => this.EvenTaskCompleted(returnTask.Result), TaskContinuationOptions.AttachedToParent);
      }
      else
      {
         // we have an odd taks
         continueTask1 = returnTask.ContinueWith<MyOutput>(ct => this.OddTaskCompleted(returnTask.Result), TaskContinuationOptions.AttachedToParent);
      }

      // is the taskNum divisible by 3? Add another task to perform after the 1st continue task is done.
      if (taskNum % 3 == 0)
      {
         if (continueTask1 != null) // make sure we have a continue task defined
         {
            continueTask1.ContinueWith(ct => fnDivideByThree(returnTask.Result), TaskContinuationOptions.AttachedToParent);
         }
      }

      // add the returned task object to our list.
      workerTasks.Add(returnTask);


      // build a query to see how many active tasks we have
      var qry = from a in workerTasks
               where !a.IsCompleted && !a.IsFaulted && !a.IsCanceled
               select a;
      // get the count of the running tasks
      int runningTaskCount = qry.Count();

      // do a compare against the maximum number of tasks we want to run at the same time
      if (runningTaskCount >= maxTasksToRunSimultaneously)
      {
         // wait until 1 of the tasks finishes
         System.Threading.Tasks.Task.WaitAny(qry.ToArray());
      }

   }

   Console.WriteLine("All tasks submitted");

   // optional- wait for all the tasks to complete.
   System.Threading.Tasks.Task.WaitAll(workerTasks.ToArray());

   // do something with the output of all the tasks that were run
   Console.WriteLine("All tasks completed");

   // iterate through each of the worker tasks in our List object
   foreach (var myTask in workerTasks)
   {
      Console.WriteLine(string.Format("Task {0} completed", myTask.Result.TaskNum));
   }
}

Define the task function.

private MyOutput Task1(int x)
{
   // create the return object
   MyOutput myOutput = new MyOutput();
   // set properties on the return object
   myOutput.TaskNum = x;

   // generate a random wait time. The _random object is initialized previously
   int waitTime = this._random.Next(10, 10000);

   // output info to console
   Console.WriteLine(string.Format("Starting task {0}, wait time: {1}", myOutput.TaskNum, waitTime));

   // simulate some work
   System.Threading.Thread.Sleep(waitTime);

   // output info to console
   Console.WriteLine(string.Format("Task {0} ending", myOutput.TaskNum));

   // return our output object
   return myOutput;
}

Define some methods to be called after the tasks are completed.

private MyOutput OddTaskCompleted(MyOutput myOutput)
{
   Console.WriteLine(string.Format("Odd task {0} ending", myOutput.TaskNum));

   return myOutput;
}

private MyOutput EvenTaskCompleted(MyOutput myOutput)
{
   Console.WriteLine(string.Format("Even task {0} ending", myOutput.TaskNum));

   return myOutput;
}

Define a class/structure to be used as the output of the tasks.

// a class/structure to use for our returned task
public class MyOutput
{
   public int TaskNum;         
}