C# CountdownEvent

Summary: in this tutorial, you’ll learn how to use the C# CountdownEvent class to wait for a specified number of events before continuing execution.

Introduction to the C# CountdownEvent class

The CountdownEvent unblocks a waiting thread until it receives the number of events. To use the CountdownEvent class, you follow these steps:

First, create a new instance of the CountdownEvent class, passing an initial count value:

var countdownEvent = new CountdownEvent(3);Code language: C# (cs)

Second, create one or more threads that will signal an event. Each thread needs to call the Signal() method of the CountdownEvent object to signal an event:

// inside a thread
countdownEvent.Signal();Code language: C# (cs)

The Signal() method decrements the initial count by one.

Third, call the Wait() method of the CountdownEvent object on the waiting threads. The Wait() method will block these threads until the count reaches zero:

// on the waiting thread
countdownEvent.Wait();Code language: C# (cs)

If an exception occurs that causes the Signal() method not to be called, the Wait() method will block the waiting thread indefinitely.

Therefore, you should always call the Signal() method even if an exception occurs.

Also, you can set a timeout so that the Wait() method will unblock the waiting thread if a time interval has elapsed or the count of the CountDownEvent object has reached zero:

countdownEvent.Wait(timeout);Code language: C# (cs)

Finally, call the Dispose() method once you’re done using the CountdownEvent object to release any resources associated with it:

countdownEvent.Dispose();Code language: C# (cs)

The following example demonstrates how the CountdownEvent class works:

using static System.Console;

var countdownEvent = new CountdownEvent(3);

void DoWork(int id)
{
    Thread.Sleep(1000);
    WriteLine($"Completed the task {id} ");
    countdownEvent.Signal();

}

// create three threads that executes the DoWork
for (int i = 1; i <= 3; i++)
{
    var id = i;
    var thread = new Thread(() => DoWork(id));
    thread.Start();
}


// block the main thread
countdownEvent.Wait();

// execute this once three event has been signaled
Console.WriteLine("All thread completed.");

countdownEvent.Dispose();Code language: C# (cs)

Output:

Completed the task 2
Completed the task 1
Completed the task 3
All thread completed.Code language: C# (cs)

How it works.

First, create a new CountdownEvent object with the initial count 3:

var countdownEvent = new CountdownEvent(3);Code language: C# (cs)

Second, define the DoWork() method that simulates a task taking one second to complete:

void DoWork(int id)
{
    Thread.Sleep(1000);
    WriteLine($"Completed the task {id} ");
    countdownEvent.Signal();
}Code language: C# (cs)

The DoWork() calls the Signal() method of the CountdownEvent object to decrement the count once it completes.

Third, create three threads that execute the DoWork() method:

for (int i = 1; i <= 3; i++)
{
    var id = i;
    var thread = new Thread(() => DoWork(id));
    thread.Start();
}Code language: C# (cs)

Fourth, call the Wait() method on the main thread to block it until the CountdownEvent object receives three signals:

countdownEvent.Wait();Code language: C# (cs)

Once the CountdownEvent reaches zero, the main thread is unblocked that executes the rest of the code, writing a message to the console and disposing of the CountdownEvent object:

// execute this once three event has been signaled
Console.WriteLine("All thread completed.");

countdownEvent.Dispose();Code language: C# (cs)

C# CountdownEvent applications

In practice, you can use the CountdownEvent class in the following applications:

  • Parallel processing: you can process a large dataset in multiple threads and use a CountdownEvent object to synchronize the completion of all threads before proceeding to the next step in the processing pipeline.
  • File download: you can download multiple files in parallel and use a CountdownEvent object to wait until all the files have been downloaded before processing them.
  • Waiting for external events: suppose your program needs to wait for multiple external events such as network messages or sensor readings before proceeding. In this case, you can use a CountdownEvent object to ensure all signals have been received before continuing.

A practical example of C# CountdownEvent class

The following program downloads three text files concurrently from a remote server and counts the words of each. It uses the CountdownEvent object to wait for the downloads to complete before returning the total word count of three files:


var urls = new List<string>()
{
    "https://www.ietf.org/rfc/rfc791.txt",
    "https://www.ietf.org/rfc/rfc792.txt",
    "https://www.ietf.org/rfc/rfc793.txt"
};

var countDownEvent = new CountdownEvent(urls.Count);
int totalWordCount = 0;

async Task<int> DownloadAndCountWords(string url)
{
    Console.WriteLine($"Downloading the file {url}...");

    using var client = new HttpClient();
    string content = await client.GetStringAsync(url);

    Console.WriteLine($"File {url} downloaded.");

    int wordCount = CountWords(content);
    Console.WriteLine($"Word count of {url}: {wordCount}");

    return wordCount;
}

int CountWords(string text)
{
    var delimiters = new char[] { ' ', '\r', '\n' };
    return text.Split(delimiters, StringSplitOptions.RemoveEmptyEntries).Length;
}


urls.ForEach(url =>
{
    Task.Run(async () =>
    {
        int wordCount = await DownloadAndCountWords(url);
        Interlocked.Add(ref totalWordCount, wordCount);
        
        // signal an event
        countDownEvent.Signal();
    });

});

countDownEvent.Wait(TimeSpan.FromSeconds(3));
countDownEvent.Dispose();

Console.WriteLine($"Total word count: {totalWordCount}");
Console.WriteLine("Press any key to exit.");
Console.ReadKey();Code language: C# (cs)

Output:

Downloading the file https://www.ietf.org/rfc/rfc791.txt...
Downloading the file https://www.ietf.org/rfc/rfc792.txt...
Downloading the file https://www.ietf.org/rfc/rfc793.txt...
File https://www.ietf.org/rfc/rfc792.txt downloaded.
Word count of https://www.ietf.org/rfc/rfc792.txt: 3714
File https://www.ietf.org/rfc/rfc791.txt downloaded.
Word count of https://www.ietf.org/rfc/rfc791.txt: 11243
File https://www.ietf.org/rfc/rfc793.txt downloaded.
Word count of https://www.ietf.org/rfc/rfc793.txt: 21460
Total word count: 36417
Press any key to exit.Code language: C# (cs)

Summary

  • Use C# CountdownEvent to allow threads to wait until a specified number of events have been signaled.
Was this tutorial helpful ?