Using the AutoResetEvent class to wake multiple threads in C#.

“I’ve been waiting for you, Obi Wan.” – Darth Vader.

One of the big differences between a ManualResetEvent and an AutoResetEvent (WaitHandles) in the .NET Framework is that a ManualResetEvent can wake up multiple waiting threads, whilst an AutoResetEvent can only wake up one of the waiting threads.

When set/triggered, the ManualResetEvent stays in that state until manually reset (hence the “manual” part). As such, all of the threads waiting on that event will observe the event moving to the signalled state – and then the waiting threads will release and continue execution.  This is useful for signalling one off events – e.g. a stop/exit event. An AutoResetEvent on the other hand – as soon as a thread releases, the event will automatically reset. This is handy for pulsing a thread – waking the thread up to execute a task before going back to sleep to wait for the next occurrence of that event.

Here’s an example.

using System;
using System.Threading;

namespace ConsoleApplication2
{
	class Program
	{
		private static AutoResetEvent _eventPulse = new AutoResetEvent(false);
		private static ManualResetEvent _eventStop = new ManualResetEvent(false);

		static void Main(string[] args)
		{
			Thread tThread1, tThread2, tThread3;

			// Start Threads
			tThread1 = new Thread(ThreadLoop);
			tThread1.Start();
			tThread2 = new Thread(ThreadLoop);
			tThread2.Start();
			tThread3 = new Thread(ThreadLoop);
			tThread3.Start();

			for (int iIndex = 0; iIndex < 4; iIndex++)
			{
				// Signal Thread to Execute
				Thread.Sleep(2000);

				_eventPulse.Set();
			}

			Thread.Sleep(2000);

			// Signal Threads to Exit
			_eventStop.Set();

			// Wait for Threads to Exit
			Thread.Sleep(500);
		}

		static void ThreadLoop()
		{
			Console.WriteLine("{0} Thread Start {1}", DateTime.Now.ToString("hh:MM:ss"), Thread.CurrentThread.ManagedThreadId);

			while (true)
			{
				// Wait For Events
				if (WaitHandle.WaitAny(new WaitHandle[] { _eventStop, _eventPulse }) == 0)
				{
					// Wait Event 0 (_eventStop)
					// Exit Loop
					Console.WriteLine("{0} Thread Terminating {1}", DateTime.Now.ToString(&quot;hh:MM:ss&quot;), Thread.CurrentThread.ManagedThreadId);
					break;
				}

				Console.WriteLine("{0} Thread Executing {1}", DateTime.Now.ToString(&quot;hh:MM:ss&quot;), Thread.CurrentThread.ManagedThreadId);
			}
		}
	}
}

This gives an output similar to that shown below. Three new threads are created. Each thread waits on an AutoResetEvent and a ManualResetEvent. As you can see, as the AutoResetEvent is triggered only one thread releases and continues execution. Two seconds later when the AutoResetEvent is triggered again, the next thread thread releases and continues execution. When the ManualResetEvent triggers however, all of the waiting threads release and continue execution.

11:12:30 Thread Start 3
11:12:30 Thread Start 5
11:12:30 Thread Start 4
11:12:32 Thread Executing 3
11:12:34 Thread Executing 5
11:12:36 Thread Executing 4
11:12:38 Thread Executing 3
11:12:40 Thread Terminating 4
11:12:40 Thread Terminating 5
11:12:40 Thread Terminating 3

That’s great, but what if I wanted all three threads to release upon the AutoResetEvent being triggered? Well, let’s first look at why the AutoResetEvent doesn’t have this functionality.

The key functionality of the AutoResetEvent is that the event resets automatically – it’s quite well named when you think about it. When the first thread that is waiting on the AutoResetEvent releases, the AutoResetEvent is automatically reset. If there are no threads waiting on the AutoResetEvent, then it stays triggered until a thread does attempt to wait on the AutoResetEvent. For the AutoResetEvent to release all waiting threads, it would need to track how many threads are waiting on the AutoResetEvent. Assuming the logic could be changed to do this, the difficulty is that while the AutoResetEvent is waiting for all of the signalled threads to release, one of the signalled threads may have already looped back around and tried to wait again. What happens when you wait on an event that is already triggered? The thread immediately releases and continues execution.

What does that mean in English? If the behaviour of the AutoResetEvent triggered all waiting threads, you may find a waiting thread triggered multiple times depending on the configuration of the loop, and the time taken for all other threads to respond. Not nice.

However, that doesn’t mean that the need for waking multiple threads with an AutoResetEvent disappears. I had a requirement for that recently. I had a job system where jobs running jobs could be triggered by an event. However, some jobs would need to be triggered by the same event. The example here is triggering different “turn light on” jobs based on a sensor in an alarm system. As the sensor activates, I needed multiple jobs to be triggered from the one event. I’ve seen a few code examples on how to deal with this, but none of them really met the requirements. What were those requirements you ask?

  1. Retain the behaviour of the AutoResetEvent. I need the threads to be triggered once per event, before looping and waiting on the same event again.
  2. Retain the ability to use the WaitHandle.WaitAll/WaitAny functions, as I’m generally waiting on multiple events (wait for a stop event, and a trigger event).

I created a small EventManager class to solve this problem. The alarm system only needed to trigger one event. Each thread only needed to listen on one event. Perfect.

For the components that need to generate the events – rather than instantiating a new AutoResetEvent by themselves, they call EventManager.GenerateAutoResetEvent(). The EventManager will create a new AutoResetEvent and add it to its internal events list (_dEvents). When that component is finished with the event, it can call EventManager.ReleaseAutoResetEvent() and the event is cleaned up.

For the components that need to wait on the events. Rather than waiting on the same AutoResetEvent, they instead pass the original event as input to EventManager.GenerateChildEvent(). This will generate a new waiting thread specific AutoResetEvent. This means that each waiting thread will actually have its own instance of AutoResetEvent, rather than all attempt to wait on a single instance (which we know doesn’t meet the requirement). How do these new child events get triggered? Easy.

When the component that generates the event needs to signal the event – rather than simply calling the Set() method of the AutoResetEvent, it now passes that event as input to the EventManager.SetAll() method. This will trigger each of the child AutoResetEvents for the parent AutoResetEvent.

The result? All waiting threads receive the trigger and only trigger once before their individual event is automatically reset.

When those thread are finished they can simply call EventManager.ReleaseChildEvent() and pass in the child event only.

using System;
using System.Collections.Generic;
using System.Threading;

namespace ConsoleApplication2
{
	public class EventManager
	{
		private static object _oLockEvents = new object();
		private static Dictionary<AutoResetEvent, List<AutoResetEvent>> _dEvents = new Dictionary<AutoResetEvent, List<AutoResetEvent>>();

		public static AutoResetEvent GenerateAutoResetEvent()
		{
			AutoResetEvent autoResetEvent = new AutoResetEvent(false);

			lock (_oLockEvents)
			{
				_dEvents.Add(autoResetEvent, new List<AutoResetEvent>());
			}

			return autoResetEvent;
		}

		public static void ReleaseAutoResetEvent(AutoResetEvent autoResetEvent)
		{
			lock (_oLockEvents)
			{
				_dEvents.Remove(autoResetEvent);
			}
		}

		public static AutoResetEvent GenerateChildEvent(AutoResetEvent autoResetEvent)
		{
			AutoResetEvent autoResetEventChild = new AutoResetEvent(false);

			lock (_oLockEvents)
			{
				_dEvents[autoResetEvent].Add(autoResetEventChild);
			}

			return autoResetEventChild;
		}

		public static void ReleaseChildEvent(AutoResetEvent autoResetEventChild)
		{
			lock (_oLockEvents)
			{
				foreach (List<AutoResetEvent> lAutoResetEvents in _dEvents.Values)
				{
					if (lAutoResetEvents.Contains(autoResetEventChild))
					{
						lAutoResetEvents.Remove(autoResetEventChild);
						break;
					}
				}
			}
		}

		public static void SetAll(AutoResetEvent autoResetEvent)
		{
			lock (_oLockEvents)
			{
				foreach (AutoResetEvent autoResetEventChild in _dEvents[autoResetEvent])
				{
					autoResetEventChild.Set();
				}
			}
		}
	}
}

It was a trivial modification to incorporate this class – simply changing the instantiation of the AutoResetEvent, changing the Set() command, and adding a new line to instantiate a thread specific child event. This allows the multiple threads to all wait on the same source AutoResetEvent and all release after one trigger. Here’s the same example again, although this time with the new EventManager class added and a couple of tiny changes to the original class.

using System;
using System.Threading;

namespace ConsoleApplication2
{
	class Program
	{
		private static AutoResetEvent _eventPulse = EventManager.GenerateAutoResetEvent();
		private static ManualResetEvent _eventStop = new ManualResetEvent(false);

		static void Main(string[] args)
		{
			Thread tThread1, tThread2, tThread3;

			// Start Threads
			tThread1 = new Thread(ThreadLoop);
			tThread1.Start();
			tThread2 = new Thread(ThreadLoop);
			tThread2.Start();
			tThread3 = new Thread(ThreadLoop);
			tThread3.Start();

			for (int iIndex = 0; iIndex < 4; iIndex++)
			{
				// Signal Thread to Execute
				Thread.Sleep(2000);

				EventManager.SetAll(_eventPulse);
			}

			Thread.Sleep(2000);

			// Signal Threads to Exit
			_eventStop.Set();

			// Wait for Threads to Exit
			Thread.Sleep(500);

			EventManager.ReleaseAutoResetEvent(_eventPulse);
		}

		static void ThreadLoop()
		{
			// Create Thread Specific Child Event
			AutoResetEvent threadEvent = EventManager.GenerateChildEvent(_eventPulse);

			Console.WriteLine("{0} Thread Start {1}", DateTime.Now.ToString("hh:MM:ss"), Thread.CurrentThread.ManagedThreadId);

			while (true)
			{
				// Wait For Events
				if (WaitHandle.WaitAny(new WaitHandle[] { _eventStop, _threadEvent }) == 0)
				{
					// Wait Event 0 (_eventStop)
					// Exit Loop
					Console.WriteLine("{0} Thread Terminating {1}", DateTime.Now.ToString("hh:MM:ss"), Thread.CurrentThread.ManagedThreadId);
					break;
				}

				Console.WriteLine("{0} Thread Executing {1}", DateTime.Now.ToString("hh:MM:ss"), Thread.CurrentThread.ManagedThreadId);
			}

			EventManager.ReleaseChildEvent(_threadEvent);
		}
	}
}

Here’s the result. You can see the difference in this output – the child threads are now releasing at the same time based on a “single” AutoResetEvent.

04:12:01 Thread Start 5
04:12:01 Thread Start 4
04:12:01 Thread Start 3
04:12:03 Thread Executing 5
04:12:03 Thread Executing 4
04:12:03 Thread Executing 3
04:12:05 Thread Executing 3
04:12:05 Thread Executing 5
04:12:05 Thread Executing 4
04:12:07 Thread Executing 5
04:12:07 Thread Executing 3
04:12:07 Thread Executing 4
04:12:09 Thread Executing 4
04:12:09 Thread Executing 5
04:12:09 Thread Executing 3
04:12:12 Thread Terminating 4
04:12:12 Thread Terminating 3
04:12:12 Thread Terminating 5

You could have also achieved the same result by extending the EventWaitHandle class, which is the base class of AutoResetEvent. AutoResetEvent itself is a sealed class that can’t be directly extended. If you take that path, when you call the base EventWaitHandle constructor, be sure to specify AutoReset as the event mode.

~ Mike

One thought on “Using the AutoResetEvent class to wake multiple threads in C#.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s