Signaling using EventWaitHandle
Wait handles provide simple and powerful thread synchronization using signaling, where threads wait for each other's signal to bring them out of the wait state. Wait handles can be local or named system wide. They have two modes:
AutoReset and
ManualReset modes. In
AutoReset mode, a single thread is released and the event automatically gets reset. In
ManualReset mode, threads are released until the event gets reset manually. You'll see examples of both. Here's the
AutoReset example:
// AutoResetEvent example
class Program
{
private static EventWaitHandle ewh =
new EventWaitHandle(false, EventResetMode.AutoReset);
static void Main(string[] args)
{
Thread th;
for (int i = 0; i < 2; i++)
{
th = new Thread(new ThreadStart(PrintThreadId));
th.Start();
}
for (int i = 0; i < 2; i++)
{
Console.ReadLine();
ewh.Set();
}
}
public static void PrintThreadId()
{
Console.WriteLine("Thread ID:{0} waiting,"
Thread.CurrentThread.ManagedThreadId);
ewh.WaitOne();
Console.WriteLine("Thread ID:{0} enters code block,"
Thread.CurrentThread.ManagedThreadId);
}
}
The program outputs the following text:
Thread ID:3 waiting
Thread ID:4 waiting
<enter key here>
Thread ID:3 enters code block
<enter key here>
Thread ID:4 enters code block
As you can see, in
AutoReset mode, each execution of the
Set method releases one thread.
In contrast, consider the code below, which demonstrates using the
ManualReset mode:
// ManualResetEvent example
class Program
{
private static EventWaitHandle ewh =
new EventWaitHandle(false, EventResetMode.ManualReset);
static void Main(string[] args)
{
Thread th;
for (int i = 0; i < 2; i++)
{
th = new Thread(new ThreadStart(PrintThreadId));
th.Start();
}
Console.ReadLine();
ewh.Set();
}
public static void PrintThreadId()
{
Console.WriteLine("Thread ID:{0} waiting,"
Thread.CurrentThread.ManagedThreadId);
ewh.WaitOne();
Console.WriteLine("Thread ID:{0} enters code block,"
Thread.CurrentThread.ManagedThreadId);
}
}
Finally, here's the output of the ManualReset mode program:
Thread ID:3 waiting
Thread ID:4 waiting
<enter key here>
Thread ID:4 enters code block
Thread ID:3 enters code block
Choosing a Synchronization Technique
Choosing the appropriate synchronization technique isn't difficult, but because there are so many possibilities, it can be difficult to remember which technique is appropriate for any particular situation. Table 2 can help; it shows a summary of the various synchronization techniques, along with each technique's unique features.
Table 2. Synchronization Techniques: This quick and handy guide shows the main features of each synchronization technique to help you determine which technique might be the most appropriate in a given situation.
Technique |
Main Feature |
Lock/Monitor |
Synchronizes access to code regions. |
Mutex |
Same as Lock/Monitor but can work system-wide. |
Interlocked |
Use when performing atomic arithmetic operations and comparisons. Avoid dirty read scenarios. |
Read—Writer |
Useful when there are many read-only threads but fewer writer threads. |
Semaphores |
Controls the number of threads that can access a resource at any point in time. |
EventWaitHandle |
Threads participate in synchronization by signaling each other. |
With all these synchronizing techniques in hand, you'll find that you can safely introduce threading operations to your applications, while also limiting the possibility of threading problems.