Introduction
As all of you know, threads allow a program to be split into two or more simultaneously running tasks. This article contains the design and implementation of a Worker Thread static library. This Worker Thread static library just does a special work like read a file or log some messages into a log file etc. Programmers can use this worker thread library by just including it, if they want to perform a work in a separate thread in their program. In this Worker Thread library I am using a worker thread manager and many worker threads. The worker thread manager will assign a particular work to a free worker thread or simply manage all worker threads. The number of worker threads under a worker thread manager can be set. Each worker thread will perform a particular work. In this WorkerThread library, there is also a WorkerThread queue. You can add your work item into the queue first. The worker thread manager will take (pop) work items from the queue and assign them to free worker threads. The worker thread will perform the work assigned to it.
Class Diagram
Class Description
WorkerThreadMgr
The WorkerThreadMgr
is a thread class derived from CWinThread
. This class will poll the WorkerThreadQueue
continuously and pop data from it. This class also creates the worker threads on demand and gives the work item read from the queue to A free thread. It contains a WorkerThreadMap
with thread ID as the map key and a pointer to WorkerThread
as the map value. The structure of the map is given below:
Map ID | Map Value |
Thread ID | Pointer to WorkerThread |
The class declaration for WorkerThreadMgr
is:
class WorkerThreadMgr : public CWinThread
{
.............
bool AddWorkItem( WorkItem* pWorktem_i );
bool Terminate();
bool IsRunning();
private:
bool KillWorkerThreads();
private:
WorkerThreadQueue<WorkItem*,WorkItem*> m_WorkQueue;
CMapDWordToWorkerThread m_WorkerThreadMap;
.............
Applications can put the WorkItem
object into the WorkerThread
queue using the AddWorkItem
function.
The InitInstance
function is overridden from the CWinThread
class. This will be the thread function of this class. This function is called when the constructor method creates the thread using a CreateThread()
call. The queue polling for a worker thread will be done here. During the queue polling, this function will iterate the worker thread map to get a free thread. If it does not get a worker thread with a status as free from the map, then it will create a new worker thread and add the worker thread into the map. Also it will set the thread status as running. If it gets a free worker thread from the map, then it will start that particular thread and set its status as running.
The Terminate()
function will set the thread status of the WorkerthreadMgr
as not running. This function is synchronized using a critical section object.
Since WorkerThreadMgr
itself is a thread, we need to know its running status. The IsRunning
function will return the thread status (running or not) of the WorkerThreadMgr
. This function is synchronized using a critical section object.
The KillWorkerThreads
private function will iterate the WorkerThread
map and kill all worker threads in the map. This function will call the Terminate()
function to terminate the WorkerThread
.
WorkerThread
This also is a CWinThread
derived class and it is created by the worker thread manager. The main functionality of this class is to receive the DO_WORK
message from the WorkerThreadMgr
. The OnDoWork()
function is called when the DO_WORK
message is received.
class WorkerThread : public CWinThread
{
.............
afx_msg LRESULT OnDoWork( WPARAM wParam_i, LPARAM lParam_i );
afx_msg LRESULT OnExitThread( WPARAM wParam_i, LPARAM lParam_i );
bool IsIdle();
bool Terminate();
The OnDoWork
is a message handler function that is called when the WorkerThread
receives the DO_WORK
message. This function mainly performs the work. So it will call the DoWork()
of the WorkItem
class.
Similarly, the OnExitThread
message handler function is called when receiving the EXIT_WORKERTHREAD
message to terminate the WorkerThread
.
WorkerThreadQueue
This class contains the generic implementation of the queue data structure using the CList
class of MFC. It uses templates so that it can hold any data type. This class also provides a critical section object to synchronize the Push and Pop operations from different threads. The class declaration is like below:
template<class TYPE, class ARG_TYPE>
class WorkerThreadQueue
We can add (push) and pop work items from the queue. The Pop()
function returns an element from the queue. The push and pop operations are synchronized using the critical section object.
void Push( ARG_TYPE Item_i )
{
::EnterCriticalSection( &m_CriticalSection );
m_Container.AddTail( Item_i );
::LeaveCriticalSection( &m_CriticalSection );
}
ARG_TYPE Pop()
{
::EnterCriticalSection( &m_CriticalSection );
ARG_TYPE WorkItemData = m_Container.RemoveHead();
::LeaveCriticalSection( &m_CriticalSection );
return WorkItemData;
}
WorkItem
The WorkItem
is a generic class to do a special work. This class has a pure virtual DoWork()
function called from the OnDoWork()
of WorkerThread
. We can create a class of type WorkItem
and override the DoWork()
function to perform the specific work.
class WorkItem
{
public:
virtual ~WorkItem() {};
virtual void DoWork() = 0;
};
How to Use It?
After building the WorkerThread
source, it will generate a WorkerThread.lib. Programs which want to use this library must include this WorkerThread.lib. The classes that do special work (for example, the FileLogger
logs messages into a file) shall be derived from the WorkItem
parent class to get the DoWork()
method as an overridden function. Also the WorkItem
(Message
in the case of FileLogger
) shall be put into the queue of the WorkerThread
using the AddWorkItem()
of WorkerThreadMgr
.