The Proactor Pattern is an asynchronous event handlingpattern. This pattern dispatches multiple events from multiple threads to the corresponding event handlers. This allows the application to have multiple operations running simultaneously without requiring the application to have a corresponding number of threads. Besides, it creates a "low coupling" and "high cohesion" between objects or a layered architecture.
Detailed information can be found in this book: Pattern-Oriented Software Architecture (Volume 2).
Some time ago, I implemented the Proactor pattern inC++ (STL), and wondered if I could make thispattern in C# and .NET. Since the .NET framework already provides a set of functionality, it became less time consuming than expected.
I'm sure (almost) every programmer could understand this pattern.
However, a set of programming and design skills are required which makes it easier to understand and to use this event handlingpattern. Here is the set of skills which makes it easier to understand:
You should know a bit about:
Don't get daunted by me. It's reasonable, I think...
Use this pattern if:
This is the class diagram of the Proactor pattern.
The idea of this example is to let the Bird
fly (in software, of course). TheBird
will receive a FlyEvent
from another Bird
and will call its "fly away" method.
Dispatcher
: the dispatcher dispatches the received events to theDispatchable
s.Dispatchable
: a Dispatchable
object could receive events from the dispatcher.PriorityQueue
: this queue contains the enqueued events (ordered in separate priority queues).Registry
: contains the registered Dispatchable
objects.IDipatchable
: the interface to the Dispatchable
objects.Bird
: the Dispatchable
object which will receive asynchronous events from the dispatcher.Here are the sequence diagrams:
Here is a set of instructions which must be done before using the Proactor Pattern.
Dispatchable
object and inherit from theIDispatchable
.
public class Bird : IDispatchable { /// <summary> /// The dispatchable of the bird. /// </summary> private Dispatchable _dispatchable = new Dispatchable();
Why not inherit directly from the Dispatchable
object?
IDispatchable
interface.
/// <summary> /// The handle event of the dispatchable /// </summary> /// <param name="ievent">the event to handle</param> public void HandleEvent(IEvent ievent) { Debug.Assert(ievent != null); if (ievent is FlyEvent) {
IDispatchable
at the dispatcher (the Bird
does this in its constructor).
/// <summary> /// Constructor /// </summary> public Bird() { // this will register the Bird Dispatcher.GetInstance.Register(this); }
The heart of the Proactor Pattern is theDispatcher
. This Dispatcher
dispatches the enqueued events to the registeredDispatchable
s in a (separate) thread.
/// <summary> /// Dispatches the events to the dispatchable items /// </summary> private void Dispatch() { // thread must be running. while (_dispachThread.ThreadState == System.Threading.ThreadState.Running) { // wait for signal _autoResetEvent.WaitOne(); // the queue should have at least one queue item available if( _queue.Count >= 1 ) {
The Dispatcher
is a Singleton.
/// <summary> /// This method will return the Dispatchers instance /// This is a Singleton pattern. /// </summary> public static Dispatcher GetInstance { get { lock (_instanceLock) { // if the instance exists, then return the // already created disptacher if (_instance == null) { // first time for the singleton _instance = new Dispatcher(); } return _instance; } } }
Keep in mind that events with a higher priority will be dispatched first. Be careful not to create starvation.
Time x: The Dispatcher
’s queue contains 210938 events with priority "Very High".
Time x + 1 millisecond: A "Very Low" priority event has been enqueued (and no further events will be enqueued).
Time x + 32365 milliseconds: the Dispatcher
's queue still contains 21 events with a priority "Very High".
The event enqueued at (Time + 1 ms) could be outdated. Don't get disappointed. There are mechanisms to prevent starvation, like a dynamic priority queue. The best part of having your own event handling mechanism is that such problems become suddenly...a challenge.
Dispatcher
's instance.Dispatcher
. Decide the priority level of the event to enqueue.
// event fired to the eagle FlyEvent fly = new FlyEvent(); Dispatcher.GetInstance.Enqueue(_eagle.DISPATCHABLE_ID, fly, Dispatcher.Priority.Normal );
HandleEvent
of theDispatcher
.This Proactor Pattern could be extended with multiplepatterns. I’ll wait on the responses first before I make a "Part 2" of thisPattern.
This article, along with any associated source code and files, is licensed underThe GNU General Public License (GPLv3)