Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

WorkerThread Library

4.00/5 (4 votes)
7 Nov 20054 min read 1   991  
An article on a generic WorkerThread library.

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

Image 1

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 IDMap Value
Thread IDPointer to WorkerThread

The class declaration for WorkerThreadMgr is:

class WorkerThreadMgr : public CWinThread
{
    .............
    
    // This function will put the WorkItem object into the queue
    bool AddWorkItem( WorkItem* pWorktem_i );

    // Terminates the worker thread
    bool Terminate();

    // Returns the Status of the WorkerThreadMgr thread
    bool IsRunning();

private:

    // Kills the Worker Threads
    bool KillWorkerThreads();

private:

    // Queue Object
    WorkerThreadQueue<WorkItem*,WorkItem*> m_WorkQueue;

    // Map containing Thread Id as Key and WorkerThread as Value
    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
{
    .............

    // Do a specific work
    afx_msg LRESULT OnDoWork( WPARAM wParam_i, LPARAM lParam_i );

    // Exit the worker thread
    afx_msg LRESULT OnExitThread( WPARAM wParam_i, LPARAM lParam_i );

    // Check Thread is idle or not
    bool IsIdle();

    // Terminate the worker thread
    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.

// Push the WorkItem into the Queue
void Push( ARG_TYPE Item_i )
{
    ::EnterCriticalSection( &m_CriticalSection );

    m_Container.AddTail( Item_i );

    ::LeaveCriticalSection( &m_CriticalSection );
}


// Pop the WorkItem from the Queue
ARG_TYPE Pop()
// Returns the WorkItem from the Queue
// (WorkItem of a generic type)
{
    ::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() {};

    // Do a special work
    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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here