Waiting for multiple threads with CountdownEvent

A common asynchronous pattern is the pattern known as fork/join parallelism. This typically manifests by starting a number of pieces of work and later joining with that work.

A CountdownEvent is initialized with a count. Threads can block waiting on the event until the count reaches 0, at which point the CountdownEvent will be set and the threads can proceed.

In this recipe, we will create a Console Application that performs some simulated work in a loop. We will initialize a CountdownEvent to a small number of tasks, and then start simulating the work with the specified number of tasks. Each task will decrement the CountDownEvent. When the CountDownEvent reaches 0 and is signaled, we will reset the CountDownEvent with a higher count and start over until we reach the maximum number of tasks.

How to do it…

Now, let's take a look at using CoundownEvent to wait for multiple threads. Have a look at the following steps:

  1. Start a new project using the C# Console Application project template and assign ForkAndJoin as the Solution name.
  2. Add the following code snippet using directives to the top of your Program class:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
  3. At the beginning of your Program class, start by creating a static variable for the CountdownEvent and a couple of constants for the number of tasks we want to start with and the number of tasks we want to finish with.
    private static CountdownEvent _countdownEvent;
    private const int BEGIN_TASKS = 2;
    private const int END_TASKS = 6;
  4. At the bottom of the Program class, after the Main method, create a new static method called SimulateWork. This method will take an integer parameter which represents the number of tasks to create. The method will then loop to create the number of tasks specified. The tasks will just sleep for a bit and write a message to the Console. When the tasks are finished executing, to call the Signal method of the CountdownEvent to decrement the count.
    private static void SimulateTasks(int taskCount)
    {
      for (int i = 0; i < taskCount; i++)
      {
        Task.Factory.StartNew((num) =>
          {
            try
            {
              var taskNumber = (int)num;
              Thread.Sleep(2500);
              Console.WriteLine("Task {0} simultated.", 
                  taskNumber);
            }
            finally
            {
              _countdownEvent.Signal();
            }
          },i);
      }
    }
  5. In the Main method of your Program class, start with instantiating the CountdownEvent object. Pass in the Begin_Tasks constant to the CountdownEvent constructor so that the event will be signaled after two tasks.
    _countdownEvent = new CountdownEvent(BEGIN_TASKS);
  6. Next, in the Main method, create a task that executes a for loop. Each iteration of the loop should reset the CountdownEvent to the number of tasks we want to wait for. Then the task will call the SimulateWork method and wait for the tasks to finish by calling the Wait method of CountdownEvent.
    var task1 = Task.Factory.StartNew(() =>
    {
      for (int i = BEGIN_TASKS; i <= END_TASKS; i++)
      {
        Console.WriteLine("**** Start simulating {0} tasks.", i);
        _countdownEvent.Reset(i);
        SimulateTasks(i);
        _countdownEvent.Wait();
        Console.WriteLine("**** End simulating {0} tasks.", i);
      }
    });
  7. Finish up the Main method by waiting for the previous task to complete in a try block and disposing of the CountdownEvent in a finally block. Wait for the user input before exiting.
    try
    {
      task1.Wait();
      Console.WriteLine("Finished. Press <Enter> to exit.");
    }
    finally
    {
      _countdownEvent.Dispose();
    }
    Console.ReadLine();
  8. In Visual Studio 2012, press F5 to run the project. You should see output similar to the following screenshot:
    How to do it…

How it works…

The main feature of CoutndownEvent, as you have already seen, is that it can be used to signal when several tasks have been completed.

The constructor for CountDownEvent accepts an integer parameter to specify the initial count of signals that we want to wait for before triggering the event. In our case, we passed in a constant value that is equal to two.

_countdownEvent = new CountdownEvent(BEGIN_TASKS);

The number of events we are waiting for can be reset by calling the Reset method as we have done in our for loop. Each iteration of the for loop increases the number of events we are waiting for, up to the maximum number which we specified in another constant.

for (int i = BEGIN_TASKS; i <= END_TASKS; i++)
{
  Console.WriteLine("**** Start simulating {0} tasks.", i);
  _countdownEvent.Reset(i);
  SimulateTasks(i);
  _countdownEvent.Wait();
  Console.WriteLine("**** End simulating {0} tasks.", i);
}

After calling the SimulateWork method with the desired number of tasks to spin up, we wait for the tasks to complete by calling the WaitMethod on the CountdownEvent.

Finally, in the SimulateWork method, each task indicates that it has completed and decrements the count of the signals we are waiting for by calling the Signal method of CountDownEvent.

Task.Factory.StartNew((num) =>
{
  try
  {
    var taskNumber = (int)num;
    Thread.Sleep(2500);
    Console.WriteLine("Task {0} simultated.", taskNumber);
  }
  finally
  {
    _countdownEvent.Signal();
  }
},i);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.61.49