C# CountdownEvent

C# CountdownEvent is a synchronization primitive which unblocks a waiting thread when its receives signal a certain number of times. CountdownEvent is used in fork-join scenarios.

Fork Join CountdownEvent

As shown in the above diagram, master thread divides its work into 3 parallel tasks. After all parallel tasks are completed, then it joins its results and control goes back to the master thread.

How to initialize CountdownEvent

During the initialization we gives initial count to CountdownEvent constructor. This is the number of signals required to unblock a waiting thread. Below is the example.

CountdownEvent countEventObject = new CountdownEvent(3);

It stores this initial count in a variable. When this variable value is 0 then it unblock the waiting thread. Now lets see how we can decrement this variable.

Signal Method

Signal method decrements the counter of CountdownEvent. In the above example, signal method decrements the counter to 2.

CountdownEvent countEventObject = new CountdownEvent(3);
countEventObject.Signal();

Note: Remember when counter reaches 0 then only it unblocks the waiting thread.

Signal method has one another overload which takes the signal count. We can use this overload method to decrement the counter to certain number. 

countEventObject.Signal(3);

In the above example, we pass 3 as signal count in Signal method. This will decrement the counter by 3.

Wait Method

We can wait method on master thread to block the thread until it receives a signal particular number of times. Below is the syntax of wait method.

countEventObject.Wait();

Wait method has one another overload which takes the time internal. A thread only enter into the blocking state for this time interval. If waiting thread receives a signal within this time interval then it unblocks thread immediately and returns true else returns false.

countEventObject.Wait(TimeSpan.FromSeconds(4));

In the above code sample, we pass 4 seconds in wait method as parameter. This will block the thread only for 4 seconds.

CountdownEvent Example

The following example shows how to use CountdownEvent in fork-join scenarios.

class Program
{
    static void Main(string[] args)
    {
        CountdownEvent countObject = new CountdownEvent(10);
        int[] result = new int[10];


        for (int i = 0; i < 10; ++i)
        {
            int j = i;
            Task.Factory.StartNew(() =>
                {

                    Thread.Sleep(TimeSpan.FromSeconds(3));
                    result[j] = j * 10;

                    countObject.Signal();
                });
        }

        countObject.Wait();

        foreach (var r in result)
        {
            Console.WriteLine(r);
        }
            
        Console.ReadLine();

        /* Result
            * 0
            * 10
            * 20
            * 30
            * 40
            * 50
            * 60
            * 70
            * 80
            * 90 */

    }
}

In the above example we initialize the CountdownEvent with 10 initial count. We start 10 threads parallel and stores their results into a result array variable. At the end of each thread we calls the Signal method on CountdownEvent object.

We call the Wait method at the end of for loop. When all threads ends their work and calls the signal method 10 times then this thread unblock and print the result array variable into the console.

Reset Method

Reset method is used for reset the counter to a particular number after the initialization of CountdownEvent object. This method lets us reuse the same CountdownEvent object.

CountdownEvent countObject = new CountdownEvent(1);

//We have now more number of threads. 
countObject.Reset(5);

AddCount Method

This method is used for dynamically increment the initial counter of CountdownEvent. We use this method in scenarios where we have dynamic data.

For example suppose from the database we fetch the records on which we have to work on parallel. We don't know the records count in advance. So we can use AddCount method to dynamically increment the counter. Below is the example.

class Program
{
    static void Main(string[] args)
    {
        CountdownEvent countObject = new CountdownEvent(1);
            
        //fetch number of records from database.
        int totalRecords = 5;
        int[] result = new int[totalRecords];

        for (int i = 0; i < totalRecords; ++i)
        {
            countObject.AddCount();

            int j = i;
            Task.Factory.StartNew(() => 
                {
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    result[j] = j * 10;
                    countObject.Signal();
                });
        }
        countObject.Signal();

        countObject.Wait();
        foreach(var r in result)
        {
            Console.WriteLine(r);
        }

        Console.ReadLine();

        /* Result:
            * 0
            * 10
            * 20
            * 30
            * 40 */
    }
}

In the above example we initialize the CountdownEvent with 1 as initial count. After the initialization of CountdownEvent we know that we have 5 records in database. In the for loop for each item we increment the counter by using AddCount method. At the end of each thread we calls the Signal method to send signal to waiting thread.

At the end of for loop we again calls the Signal method to notify that all the threads are processed.

Summary

  1. C# CountdownEvent unblocks a waiting thread when its receives signal a certain number of times.
  2. Its is used in fork-join scenarios.
  3. Its signal count can be increment dynamically.
  4. CountdownEvent can be reuse by using Reset method.

Join our newsletter and get an occasional email with a technology and dotnetpattern.com news update.