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.
Now, let's take a look at using CoundownEven
t to wait for multiple threads. Have a look at the following steps:
ForkAndJoin
as the Solution name.Program
class:using System; using System.Threading; using System.Threading.Tasks;
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;
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); } }
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);
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); } });
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();
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);
3.147.61.49