Click here to Skip to main content
11,712,241 members (76,980 online)
Click here to Skip to main content

Creating a C++ Thread Class

, 8 Nov 2007 CPOL 140.8K 3.3K 110
Rate this:
Please Sign up or sign in to vote.
A look at platform independent threading in C++.


Recently, my brother asked me if there is an easy way to create a C++ class that facilitated object orientated threading. I have written many multi-threaded libraries in the past; however, they are all in C. C has always been my language of choice for low-level programming; I use C++ for GUI development. Although there are many excellent examples of object-oriented threading on CodeProject, none of the classes introduced suited all of my brother's needs and my curiosities. He wanted a thread class that had the following attributes:

  • It supports both event driven and interval based asynchronous threading.
  • It supports both homogeneous and specialized threading.
  • It provides a FCFS (First Come First Serve) stack-queue for posting and handling multiple tasks.
  • It is portable.
  • It is simple to implement.

To support the new class, CThread, other supporting classes were also developed. These include the CMutexClass, CEventClass, and CTask classes. The CMutexClass and CEventClass provide resource management, while the CTask class is a base class for deriving classes that support homogeneous asynchronous threading.

What is Threading?

Every process has at least one thread of control, and every process can perform at least one task at a time. A process that has more than one thread of control defines a multi-threaded process. A multi-threaded process allows multiple tasks to run asynchronously from within the environment of the process.

Resource Management—Thread Synchronization

Because threads within a multi-threaded process share the same resources, OS level control mechanisms are necessary to ensure data integrity. A loss of data integrity occurs when one thread is modifying a variable while another thread is attempting to read it, or two threads are attempting to modify the same variable at the same time. To prevent this scenario, the OS provides a Mutual Exclusion Object, known in short as a mutex. In multi-threaded applications, mutexes, deployed programmatically, prevent multiple threads from accessing a single resource at the same time. When a thread needs access to a resource, it must first acquire a mutex. Once a thread has acquired a mutex, other threads attempting to acquire the same mutex are blocked, and placed in a low-CPU usage wait state. Once a thread has completed data access, it releases the corresponding mutex; this allows other threads to acquire it and access the corresponding data.

Poor implementations of mutexes can result in resource starvation, also known as deadlock. Resource starvation occurs when one or more threads are competing for the same resource.


Thread A Thread B
Acquires mutex(1) to modify data item 1 Acquires mutex(2) to modify data item 2
Wants mutex(2) to view data item 2 Wants mutex(1) to view data item 1

A deadlock occurs in the example above because thread A is blocked trying to acquire mutex(2), which is held by thread B. Thread B is blocked trying to acquire mutex(1), which is blocked by thread A.

Like mutexes, condition variables, in UNIX, are another form of synchronization mechanism. Condition variables allow threads to rendezvous. They allow one thread to notify another that a change has occurred. In Windows, these are events.

Operating System Calls

The following table is a list of the various functions used to implement threading in the CMutexClass, CEventClass, CTask, and CThread classes.

Function OS Description Class Used in
CreateThread Windows Creates a Windows thread CThread
pthread_create UNIX - POSIX THREADS Creates a UNIX thread CThread
pthread_join UNIX - POSIX THREADS Waits for a UNIX thread to terminate CThread
pthread_attr_init UNIX - POSIX THREADS Sets a thread attribute structure to default CThread
pthread_attr_setstacksize UNIX - POSIX THREADS Sets the stack size value of the thread attribute structure CThread
WaitForSingleObject Windows Waits for an object to be signaled CThread, CMutexClass, CEventClass
CreateMutex Windows Creates a named or unnamed mutex CMutexClass
CloseHandle Windows Releases resources allocated to a Windows handle CMutexClass, CEventClass, CThread
ReleaseMutex Windows Releases a previously acquired mutex locked by WaitForSingleObject CMutexClass, CEventClass
pthread_mutexattr_init UNIX - POSIX THREADS Initializes a mutex attribute structure CMutexClass, CEventClass
pthread_mutex_init UNIX - POSIX THREADS Initializes a mutex using a provided attribute structure CMutexClass, CEventClass
pthread_mutex_lock UNIX - POSIX THREADS Locks a mutex CMutexClass, CEventClass
pthread_mutex_unlock UNIX - POSIX THREADS Unlocks a mutex previously locked by pthread_mutex_lock CMutexClass, CEventClass
pthread_mutex_destroy UNIX - POSIX THREADS Releases resources allocated to a mutex CMutexClass, CEventClass
CreateEvent Windows Creates a Windows event object CEventClass
SetEvent Windows Sets a Windows event object to signaled CEventClass
pthread_cond_signal UNIX - POSIX THREADS Unblocks a thread blocked on pthread_cond_wait CEventClass
pthread_cond_wait UNIX - POSIX THREADS Blocks on a condition variable CEventClass
pthread_cond_init UNIX - POSIX THREADS Initializes a condition variable CEventClass

The CMutexClass Class

The CMutexClass class encapsulates the system level mutex functions and a mutex synchronization object. Mutex creation occurs during object instantiation, with the mutex created as unblocked. The class provides two member functions, Lock and Unlock. The Lock member function locks a mutex, assigning it to the calling thread. The mutex remains locked to the calling thread until the calling thread releases it using the Unlock member function. Threads that attempt to acquire a locked mutex by calling the Lock member function are blocked, and placed into a low CPU consumption wait state until the blocking thread releases the mutex.

Member Functions

Function Description
void CMutexClass() Constructor
void Lock() Locks a mutex object, or waits if blocked
void Unlock() Unlocks/unblocks a previously blocked mutex


int g_iStorage = 0;
CMutexClass MyMutex;

void StoreValue( int *pInt )
   MyMutex.Lock();        //the gate keeper. only one thread
                          //allowed in at a time

   g_iStorage = *pInt;    //protected data, critical code section

   MyMutex.Unlock();      //unblocks, allowing another thread to
                          //access g_iStorage


The CEventClass Class

The CEventClass class encapsulates the Windows event functions, a Windows event object, UNIX condition variable functions, and a UNIX condition variable. The functions incorporated into the CEventClass class are SetEvent and CreateEvent under Windows, and phtread_cond_init, pthread_cond_destroy, pthread_cond_signal, and pthread_cond_wait under UNIX. Event synchronization objects are called condition variables under UNIX, but for the purpose of simplification, I will refer to both condition variables and event objects as event objects.

Member Functions

Function Description
void Set() Sets an event state to signaled, notifying the blocked thread.
BOOL Wait() Places the calling thread in a blocked state until the event state is set to signaled. Returns TRUE on success, FALSE on failure.
void Reset() Resets a signaled event to unsignaled.

Example of an event object being used by a receiving thread:

CEventClass event;
//thread code


      event.Wait();     // wait for an event to occur

      // perform some task

      event.Reset();    // reset the event to un-signaled


Example of an event object used by one thread signaling another:

CEventClass event;
// change some data

   event.Set();    // notify thread that an event has occured,

                   // set event to signaled


The CTask Class and Non-Specialized Threads

In many of the thread programming examples that I have seen, data for thread processing is stored in a global variable, protected by a mutex. Instructions for operating on the data are integrated into the thread function. I define this form of threading as Specialized Asynchronous Threading (SAT). Ideally, the data and the corresponding functionality for processing the data should be encapsulated into the same object. I define this form of threading as Homogeneous Asynchronous Threading (HAT). Under HAT, threads are not specialized. For example, there would not be a printing thread and an I/O thread in a HAT solution. Instead, a single thread could perform both types of tasks because the tasks are implemented as complete objects; that is, they contain both the data and the functionality necessary to process the data. The CTask class is a base class that facilitates HAT-based threading.

typedef enum {
   TaskStatusCompleted } TaskStatus_t;

class CTask
   CMutexClass m_mutex;
   TaskStatus_t m_state;
   ThreadId_t m_dwThread;
   void SetTaskStatus(TaskStatus_t state)

   void SetId(ThreadId_t *pid)

    * Wait
    * waits for upto timeoutSeconds for a task
    * to complete
   BOOL Wait(int timeoutSeconds)
      timeoutSeconds = timeoutSeconds * 1000;
      if( Status() != TaskStatusCompleted &&
          timeoutSeconds > 0 )
         timeoutSeconds = timeoutSeconds - 100;
      if( Status() == TaskStatusCompleted ) return TRUE;
      return FALSE;

    * Where
    * returns current state of a task
   TaskStatus_t Status()
      TaskStatus_t state ;

        state = m_state;
      return state;

   void Thread(ThreadId_t *pId)

           memset(&m_dwThread,sizeof(ThreadId_t),0); }
   virtual BOOL Task()=0;

Member Functions

Function Description
m_mutex Mutex object synchronization object.
virtual BOOL Task() Called by a CThread object to perform the task.
TaskStatus_t Status() Determines the task status: TaskStatusNotSubmitted, TaskStatusWaitingOnQueue, TaskStatusBeingProcessed, or TaskStatusCompleted.
void Thread(ThreadId_t *pid) Returns the thread ID of the processing thread.
BOOL Wait(int iTimeInSeconds) Places a calling thread into a wait state until the task completes or iTimeInSeconds elapses. If a task does not complete within iTimeInSeconds, FALSE is returned; otherwise, TRUE is returned.

I have not defined the CThread class; however, its definition is not necessary to understand how it interacts with a CTask object. The list below presents an outline of how the two object types interact.

The procedure for processing a CTask object:

  • A CTask object is passed to a CThread object to be processed.
  • The CThread object places the CTask object in a First Come First Served Queue.
  • The CThread object sets the CTask object's state to TaskStatusWaitingOnQueue.
  • The CThread object pops the CTask object off of the wait queue.
  • The CThread object changes the CTask object's state to TaskStatusBeingProcessed.
  • The CThread object calls the CTask object's member function "task" to perform the task.
  • The CThread object changes the CTask object's state to TaskStateCompleted.

The CThread Class, Putting It All Together

Member Functions

Function Description
void CThread() The constructor initializes object data and starts the thread.
void ~CThread() Terminates thread if it is running, and frees resources.
BOOL Event(LPVOID lpvData) Places a data block on the event stack/queue, and notifies the object's thread that data is waiting to be processed.
BOOL Event(CTask *pTask) Places a CTask object on the event stack/queue, and notifies the object's thread that a task is waiting to be performed.
int GetEventsPending() Returns the number of events waiting on the event stack.
ThreadId_t GetId() Returns the object's thread ID.
DWORD GetErrorFlags() Returns the object's error flags. If there are no errors, a value of 0 is returned (NO_ERRORS). If there are errors, one or more of the following flags will be set: MUTEX_CREATION (a mutex object could not be created), EVENT_CREATION (an event object could not be created), THREAD_CREATION (the object's thread could not be created), ILLEGAL_USE_OF_EVENT (the Event member function was called for an interval based thread).
BOOL PingThread(DWORD dwTimeoutMilli) Determines whether the object's thread is running. Returns TRUE if the thread is running, FALSE if it is not. Timeout is in seconds.
SetPriority(DWORD dwPriority) Sets the thread's priority, Windows only.
BOOL Start() Starts the object's thread.
BOOL Stop() Stops the object's thread.
void SetIdle(DWORD dwIdle) Changes a thread's idle time in milliseconds, used with interval-based threading.
SetThreadType(ThreadType_t typ,DWORD dwIdle) Changes the thread type between ThreadTypeEventDriven and ThreadTypeIntervalDriven.
m_mutex A mutex object used for synchronization, see CMutexClass.
ThreadState_t ThreadState() Returns the state of a thread: ThreadStateBusy (the thread is processing an event), ThreadStateWaiting (the thread is waiting for a new event), ThreadStateDown (the thread is not running), ThreadStateShutingDown (the thread is in the process of shutting down).

Now that you have learned the supporting classes, it's time to look at the main class, the CThread class—the workhorse. The CThread class supports two types of threads, Event Driven and Interval Driven. An Event Driven thread is a thread that remains in a wait state, blocked on an event object, until the event object's state changes from unsignaled to signaled. A new event occurs when a different thread places a task in a CThread object's queue and notifies the object's thread by setting its event object to signaled. Once signaled, the thread wakes up and pops tasks from its event queue until the queue is empty.

The CThread object invokes the OnTask member function for each task. Tasks are processed in a First Come First Serve (FCFS) order. Hence, the first task placed in a CThread object's queue is processed first, followed by the second, and so on. A mutex object synchronizes queue access, allowing additional events to be placed on the queue while the thread is processing older ones. Once the queue is empty, the thread resets the event object to unsignaled, and returns to waiting for an event object. The CThread class supports two types of Event Driven threads: specialized and unspecialized threads, see CTask.

To implement a specialized thread, a new class must be derived from the CThread class. The derived class should contain a redefined implementation of OnTask to process the object's data types.


#include <span class="code-string">"Thread.h"</span>

In the example above, I have derived a CIncrementThread class from a CThread class. In the class definition, I redefined both the OnTask() and OnTask(LPVOID) virtual member functions. In the OnTask() implementation, I add one to the object's counter variable. The other OnTask member function takes a pointer to an integer value, and adds the pointer's value to the counter member variable. This example illustrates the two types of events that a thread can process. Because the counter variable has the potential of being accessed by more than one thread, I use the CThread::m_mutex object to ensure that it is only accessed by one thread.

HAT (Homogeneous Asynchronous Threading) threads are implemented using both the CThread and the CTask classes.


#include <span class="code-string">"Thread.h"</span>

class CTaskIncrementer: public CTask
   int counter;
   int incr;
   void SetIncr(int iValue) 
         incr = iValue;

   int GetIncrementValue()
      int incrValue;
         return incrValue;

   int GetValue()
      int counterValue = 0;
      m_mutex.Lock();    // protect the counter variable

         counterValue = counter;
         return counter;

   BOOL Task()
      ThreadId_t id;


         printf("\tthread(%ld, counter+%d=%d, counter incremented\n",
         return TRUE;

main(int argc,
   char *argv[])
   CTaskIncrementer incr;
   CThread thr;

   while( incr.GetValue() < 40 ) thr.Event(&incr);
       thread(5700, counter+2=2, counter incremented
       thread(5700, counter+2=4, counter incremented
       thread(5700, counter+2=6, counter incremented
       thread(5700, counter+2=8, counter incremented
       thread(5700, counter+2=10, counter incremented
       thread(5700, counter+2=12, counter incremented
       thread(5700, counter+2=14, counter incremented
       thread(5700, counter+2=16, counter incremented
       thread(5700, counter+2=18, counter incremented
       thread(5700, counter+2=20, counter incremented
       thread(5700, counter+2=22, counter incremented
       thread(5700, counter+2=24, counter incremented
       thread(5700, counter+2=26, counter incremented
       thread(5700, counter+2=28, counter incremented
       thread(5700, counter+2=30, counter incremented
       thread(5700, counter+2=32, counter incremented
       thread(5700, counter+2=34, counter incremented
       thread(5700, counter+2=36, counter incremented
       thread(5700, counter+2=38, counter incremented
       thread(5700, counter+2=40, counter incremented

An Interval Driven thread is a thread that wakes up at predefined intervals, checks to see whether there is a change in the environment, processes the changes in the environment, sleeps for the next interval, and then wakes up and does it all over again. To implement an interval driven thread, you derive a CThread class that redefines OnTask(LPVOID). Once the thread has been instantiated, you call the SetThreadType member function with the parameter ThreadTypeIntervalDriven and an interval in milliseconds.


#include <span class="code-string">"Thread.h"</span>

class CIncrementThread : public CThread
   int counter;

   virtual BOOL OnTask()
      ThreadId_t id;

      //don't use cout here, output could be broken up due to

      m_mutex.Lock();    // protect the counter variable

      printf("\tthread(%ld, counter++= %d, counter incremented)\n",

      return TRUE;

   int GetValue()
      int counterValue = 0;
      m_mutex.Lock();    // protect the counter variable

         counterValue = counter;
      return counter;

   void Reset()
               counter = 0;


main( int argc, 
    char *argv[] )
   CIncrementThread thr;


        thread(6104, counter++= 12, counter incremented)
        thread(6104, counter++= 13, counter incremented)
        thread(6104, counter++= 14, counter incremented)
        thread(6104, counter++= 15, counter incremented)
        thread(6104, counter++= 16, counter incremented)


There you have it, a fully functional thread object. I have tested on Linux, and the classes work fine. I have yet to test on the SunOS or any of the other UNIX platforms that these classes should support. When compiling on Windows, be sure to specify /Mt or /Mtd for code generation; this identifies your application as a multi-threaded application. For Linux, the following make file works:

LIBS=-lpthread -lrt

OBJS=Thread.cpp EventClass.cpp MutexClass.cpp main.cpp

EXECS = thread

all: $(EXECS)

thread: $(OBJS)
    $(CC) $(CFLAGS) -o thread $(OBJS) $(LIBS)

clean:; rm -f *.o $(EXECS)


  • (31 Oct 2007)
    • Added support for beginthreadex. To compile to use beginthreadex, define USE_BEGIN_THREAD. The default is to use CreateThread for thread creation. The attached project defines USE_BEGIN_THREAD in the preprocessor section.
    • I forgot CreateThread is prone to leaks if used with the C runtime functions.
  • (1 Nov 2007)
  • Made a lot of improvements:

    • I introduced more logic and ASSERTIONs to ensure the integrity of CThread objects. Both the Homogeneous and Specialized thread types can be physically set using the SetThreadType member function. If the thread type is not set, the thread will determine its type based on calls to member functions; however, this does not apply to interval-based threads. Interval-based threads must be implicitly identified using the SetThreadType member function. The new integrity tests are implemented to ensure usage consistency with a CThread object.
    • New member functions AtCapacity and PercentCapacity were added to determine if a thread is truly busy. AtCapacity will return TRUE under one of two conditions: the thread is processing an event and its queue is full, or the thread is not running. PercentCapacity returns the percentage full for an object's queue. These new functions allow thread objects to be placed in arrays and tasked based on their workloads.
    • New member function SetQueueSize allows a CThread object's queue to be resized. The function will return false if the number of elements pending on a queue is greater than the requested queue size.
    • The Event member function has been modified to verify that a thread is running before posting an event. This eliminates the need to call PingThread when passing an event for the first time.
    • Error flags are automatically reset when certain member functions are called; this isolates error occurrences to specific call sequences.
  • (2 Nov 2007)
    • Removed unnecessary code, for UNIX platforms, in the CEventClass class per On Freund's comments, see message below.
  • (2 Nov 2007)
    • Added try/catch blocks to ensure proper use of all classes per On's message.
  • (8 Nov 2007)
  • This is the last installment of changes for this article. I am moving onto my next article. Since it will be using these classes, additional fixes will be posted there.

    • I forgot to remove a couple sequential calls to m_event.Reset that were meant for testing the CEventClass' try-catch block. This will only affect non-Windows platforms. Also, relocated m_event.Reset to immediately after m_event.Wait. Since we are dealing with a queue, this could cause a bottleneck. I commented out the old code, just in case someone prefers it the old way.
    • The example, main.cpp, contained a couple sequential calls to m_mutex.lock which would throw an exception; these were meant for testing the CMutexClass' try-catch block. They have been removed.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Walter Capers
United States United States
Currently I am a Software Architect for Compuware specializing in software security
Languages: C/C++, FORTRAN, COBOL, Java
Libraries: OpenGL, MFC, X11 (X-Windows), WIN32

Platform Experience: AIX, HP-UX, SunOS, Open VMS, AS400, OSF, AIX, SGI, Linux, Windows CE, Windows

I have a strong background in 3D graphics programming, TCP/IP development, threading, cross platform development, encryption and secured communications, and system level programming.

In the early years before OpenGL made its way to the PC world, I authored one of the first high performance double buffered 3D graphics engines that supported both DOS and Windows providing real time 3D graphics for Engineering Technology Associate’s Finite Element Model Builder, FEMB. You can see their products at

I also hold a US patent

You may also be interested in...

Comments and Discussions

SuggestionCould you add some parallel threads function? Pin
JinxLeader4-Jun-15 2:35
memberJinxLeader4-Jun-15 2:35 
GeneralMy vote of 5 Pin
Apoorv Agrawal11-Oct-12 8:36
memberApoorv Agrawal11-Oct-12 8:36 
GeneralMy vote of 5 Pin
Vincent_Huang2-Sep-12 16:36
memberVincent_Huang2-Sep-12 16:36 
QuestionQuick guide to create threads in C++ Pin
Jessn16-Jan-12 0:47
memberJessn16-Jan-12 0:47 
AnswerRe: Quick guide to create threads in C++ Pin
Walter Capers30-Jul-12 8:33
memberWalter Capers30-Jul-12 8:33 
AnswerRe: Quick guide to create threads in C++ Pin
Walter Capers5-Sep-12 5:16
memberWalter Capers5-Sep-12 5:16 
GeneralRe: Quick guide to create threads in C++ Pin
Jessn18-Feb-14 0:11
memberJessn18-Feb-14 0:11 
Questionusing counterValue in the code Pin
Ruslan_RK30-Aug-11 20:03
memberRuslan_RK30-Aug-11 20:03 
GeneralMy vote of 4 Pin
Nicolae Mogoreanu21-Jan-11 9:24
memberNicolae Mogoreanu21-Jan-11 9:24 
QuestionMultiple Threads Usage Pin
GHop6-Oct-09 7:36
memberGHop6-Oct-09 7:36 
Generalcompiling on MinGW Pin
Lonnie5-Sep-08 2:24
memberLonnie5-Sep-08 2:24 
GeneralRe: compiling on MinGW Pin
Walter Capers5-Sep-08 3:03
memberWalter Capers5-Sep-08 3:03 
GeneralRe: compiling on MinGW Pin
Lonnie5-Sep-08 4:13
memberLonnie5-Sep-08 4:13 
GeneralRe: compiling on MinGW Pin
Walter Capers5-Sep-08 4:22
memberWalter Capers5-Sep-08 4:22 
GeneralRe: compiling on MinGW Pin
Lonnie5-Sep-08 5:26
memberLonnie5-Sep-08 5:26 
GeneralRe: compiling on MinGW Pin
Lonnie5-Sep-08 8:46
memberLonnie5-Sep-08 8:46 
GeneralThread with no event crashes ::Pop when thread is destroyed Pin
PParty29-Jul-08 18:22
memberPParty29-Jul-08 18:22 
GeneralRe: Thread with no event crashes ::Pop when thread is destroyed Pin
Walter Capers5-Sep-08 3:06
memberWalter Capers5-Sep-08 3:06 
QuestionVC6 ? Pin
P.Pronk5-Nov-07 23:19
memberP.Pronk5-Nov-07 23:19 
AnswerRe: VC6 ? [modified] Pin
Walter Capers6-Nov-07 3:34
memberWalter Capers6-Nov-07 3:34 
I wish I could but I don't have version 6.0 of Visual Studio. If I had Visual Studio 6, I would create an empty console project, add in the attached source files, and under the code generation option I would select multi-threading.

Sorry that I can't be of much help here.

-- modified at 15:37 Tuesday 6th November, 2007

Walter E. Capers

AnswerRe: VC6 ? Pin
Member 89009026-Mar-12 0:05
memberMember 89009026-Mar-12 0:05 
GeneralGood Pin
rajantawate1(http// 11:10
memberrajantawate1(http// 11:10 
QuestionWhy so much locking? Pin
Joergen Sigvardsson31-Oct-07 8:26
memberJoergen Sigvardsson31-Oct-07 8:26 
AnswerRe: Why so much locking? Pin
Walter Capers31-Oct-07 8:50
memberWalter Capers31-Oct-07 8:50 
QuestionNested mutex question Pin
prcarp31-Oct-07 8:08
memberprcarp31-Oct-07 8:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150819.1 | Last Updated 8 Nov 2007
Article Copyright 2007 by Walter Capers
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid