"BOSS-WORKERS" MULTITHREADING APPLICATION MODEL FOR .NET
Well-known "boss-workers" or "master-slaves" application model assumes that there is something who dispatches jobs (boss or master) and something who executes jobs (worker or slave). There are process level "boss-workers " model where boss and workers are processes and multithreaded "boss-workers" where everything happens inside single process. "Boss-workers" paradigm is very widely used in server applications.
I used it in variuos fields: PC-based measurement systems (software ocilloscopes) developed with C++-Builder. CRM applications - customers document generation server for telco under Tru64 (pthreads). Billing system gateway server that provides access to billing system using SOAP under OpenVMS (pthreads). All these application were developed in C++ and used Windows threads API in Win32 environment and pthreads library in Unix and OpenVMS.
Here I present simple framework for .NET to make "boss-workers"-style application development more easier (I'm using .NET Framework 2.0). To illustrate how to use framework classes I've developed few projects that are based on it. Two projects are developed with VB.NET and one with C#.
Messages
All objects in this framework communicate using messages:
Class name |
Usage |
|
TGenericMessage |
Abstract class. Contains message number m_command and optional cargo object m_user. m_user maybe used to store reference to sender or other stuff suitable for particular case. |
|
| TVoidMessage | This message type has no payload. Maybe used for primitive commands like: terminate, suspend, resume and so on (that doesn't need additional data). | |
| TClassMessage(Of T) | Generic message that contains object of arbitrary class T. | |
| TClassArrayMessage(Of T) | Generic message that contains list of objects of class T |
"Boss-workers" application model framework is very simple and contains single base class TGenericExecutor and two derived classes:
TGenericDispatcher - for boss and TGenericWorker for worker.
TGenericExecutor class
The base class - TGenericExecutor is basically .NET thread wrapper with some added behaviour. TGenericExecutor object behaviour is concentrated in thread function MainLoop. Thread function contains the message processing loop between Preambulae() and Poscriptum() calls. It is clear from the names what these calls are for.
Protected Sub MainLoop()
' make some preparations and checks
Preambulae()
Do While IsMainState()
Dim msg As TGenericMessage = Nothing
' get new message
msg = GetMessage()
If Not msg Is Nothing Then
' do message specific stuff
ProcessMessage(msg)
End If
' give other threads time to work
Thread.Sleep(0)
Loop
' do some terminating stuff
Postscriptum()
End Sub
Preamulae and Postscriptum methods are for some specific actions after the birth of object and before the death. The most important part of TGenericExecutor life is messaging loop. Within messaging loop TGenericExecutor receives messages and performs some actions in response.
Loop condition is checking thread state. Thread message queue is simple queue with FIFO rule. Thread can send messages to other threads using PostMessage method.
Boss-Workers application model classes
Both boss-worker model classes : TGenericWorker and TGenericDispatcher are based on TGenericExecutor class. Boss (TGenericDispatcher) coordinated workers using messages. There are three types of predefined messages:
- Registration message - is used by workers to tell boss that it is available
- Termination message - used to stop boss-worker system.
- Terminated message - notification message issued by worker threads to boss to tell that worker terminated.
TGenericWorker Class
This class is TGenericExecutor specialization with very primitive behaviour: TGenericWorker implements DoPreambulae method (sends registration message to boss) and DoPostscriptum (sends "terminated" message to boss) and introduces two new abstract methods:
- CanHandle
- ProcessApplicationSpecificMesssage
Virtual method CanHandle is used by boss thread to know is worker suitable for given job message. ProcessApplcationSpecificMessage is the placeholder for some application specific stuff.
TGenericDispatcher Class
TGenericDispatcher class is abstraction of boss. It has two major additions to TGenericExecutor functionality: internal message queue and pool of working threads. Internal message queue bring more flexibility working with messages. It is represented by four abstract methods:
| IsQueueEmpty | Method name says what it is for |
| Enqueue | Put message to internal queue. Realization depends on application specifics. May take into account priority of message, may create several messages from one origin. |
| Dequeue | Returns the object at the beginning of queue and removes it from queue. |
| Peek | Does the same as Dequeue without removing message. |
As I mentioned above in addition to internal message queue TGenericDispatcher has pool of workers made from two lists. One (m_avail) - for free workers and other (m_busy) for busy workers. When boss receives registration message from worker it removes it from busy-list and adds to free-list. When boss assigns job to worker it performs the opposite actions.
Other important methods are StartWorkers - to start working threads and CanStop - returns true when boss may terminate. Termination of "boss-workers" application . Boss receives termination message and dispatches it to all working threads with no difference busy or free. After that boss waits for "terminated" messages (terminate command accepted) from workers. When boss receives "terminated" message it calls CanStop method to know - is it possible to terminate itself.
Simple "boss-workers" application
This application is the simplest anybody could imagine. There is boss thread that receives messages from main application thread. There are two types of messages:
- message of type TSquareRootMessage (contains positive number to calculate square root for);
- termination message (to stop threads).
Number of workers is selected from menu. Messaging flow is shown at figure below:
As you can see, boss receives TSquareRootMessage object from main thread and redirects it to the first available worker. Worker calculates square root for given number and responds with TResultMessage object that has inside the source number and its square root value. To stop threads main thread puts TTerminationMessage object to boss message queue and after that all happens in usual way. I did two versions of this application:
- MultiTest1 project in VB.NET;
- MultiTest2 project in C#.
All projects are based on the same VB.NET classes.
Simple socket server
Here I present simple socket server based on "boss-workers" classes. There are three classes:
- TBoss - for boss;
- TSocketListener - thread that listens for incoming connections;
- TConnectionServant - thread that serves connections.
To make server little bit realistic lets it serve some hypothetic queries. The typical query processing scenario is as follows.
TSocketListener object accepts connection from some client. After that it sends created socket object to server in TAcceptedSocketMessage object. Boss tries to find available worker to serve connection and If there is free worker boss redirects this job for him immediately, otherwise message is left in internal message queue. TConnectionServant receives TAcceptedSocketMessage objects and tries to receive data (using asynchronous operation). After data is successfully received worker parses it and creates TQuery object that is again sent to boss as TSocketQueryMessage. Boss analyzes query. It may be termination command or query. In case of query server creates query ID and passes it to connection serving thread (inside TSocketResponseMessage object). Then response is transmitted to client (in synchronous manner).
Generalized socket server messaging scheme

Query processing scheme
