Windows C++ Threading Made Simple, Safe and Painless

Introduction

C++ Logo

Programming threads can be tricky in any language and OS, but the Windows C++ API offers a confusing choice of threading functions. This article aims to clarify the API, and offers some old non-MFC C++ classes which are very easy to use and continue to be useful today.

Back in The Day….

About nine years ago I wrote these classes using Visual C++ 6.0. In the years that followed, they have been refined, updated, and, most recently, tested in a Visual Studio 2010 project.

Though they were originally created for the 32 bit (x86) environment, they have been included in a 64 bit (x64) project and a Windows Mobile 2003 (and Windows Mobile 5 and 6) project.

The Objective

I aimed to create a small number of classes that would isolate their user from the Windows API (in respect to threading) and provide ease of use.

Keep it Simple

The classes offer a simplified functionality, perhaps too simple for some users:-

  • No facility to specify the stack size for the thread is included. The default size is used.
  • No facility to specify the SECURITY_ATTRIBUTES for the thread is included. The default is used.
  • No support for waiting for multiple thread instances has been included.
  • No support for thread local storage is included.

In MFC Projects

These classes do not require MFC but may be used in MFC projects, provided that no MFC code is used from within the a thread.

MFC programmers whould normally MFC’s own AfxBeginThread which correctly initialises the thread for MFC use.

Threads which do not use MFC code may be used within MFC projects safely.

I’ve never actually tested the consequences of MFC invocation from within a thread launched by these classes, but if the Authorities warn of thin ice, I tend not to organise a skating session.

The Classes

Overview

The three classes are all in Thread.cpp and use the single header file Thread.h. Note that Thread.h does not require any of the Windows header files.

Namespace

The classes are in a namespace t2a in order to avoid name collisions. You may prefer to rename it or remove the explicit namespace; note the line in Thread.cpp:-

using namespace t2a;

..and in Thread.h, note the the definitions are enclosed:-

namespace t2a
{
....
}

Calling Convention

The public functions in the classes use the _cdecl convention with the exception of the CThreadRun::Run function which uses _stdcall. These may be changed or removed to use your complier default, as you choose.

CThreadMutex

This class is used to give mutual exclusivity to a resource, used with CThreadLock.

CThreadLock

Used with CThreadMutex, this class ensures exclusive access to a resource.

To ensure that your code has exclusive access to an instance of a class, either include an instance of CThreadMutex in your class:-

class CMyClass
{
private:
   CThreadMutex m_Mutex;
public:
...

or derive your class from CThreadMutex:-

CMyClass : public CThreadMutex
{
...

To obtain a global lock, create a static instance:-

static CThreadMutex g_Mutex;

Using CThreadLock is simple. Define an instance, usually as a stack instance, using the aforementioned CThreadMutex instance. Obtain the exclusive lock, perform your actions, and release the lock. When obtaining the lock, specify a maximum time to wait for the lock to be granted.

The example below illustrates the use of CThreadLock in a class:-

class CMyClass
{
private:
   CThreadMutex m_Mutex;

public:
   void UseLock(void)
   {
                                // a lock instance:-
      CThreadLock lock(&m_Mutex);

                                // obtain an exclusive lock,
                                // wait for 30 seconds:-
      if (lock.Lock(30000)
      {
                                // execute your exclusive
                                // actions...

         lock.Unlock();         // ...and release the lock.
      }
   }
};

CThreadRun

This class allows the simple creation of thread code, and an easy means to launch and abort the thread.

To use, derive a class from CThreadRun, creating your own version of the CThreadRun::Run function.

Note that CThreadRun has an instance of CThreadMutex in order to provide thread safety to the public functions.

The constructor has no parameters.

CThreadRun();

These are the public functions; there are no public data members.

CThreadRun::Abort

Instruct the thread to abort, and specify auto deletion.

Note that this simply sets a flag which the CThreadRun::Run function can read and either exit a loop or otherwise exit, and cause the thread to end.

bool _cdecl Abort(bool auto_delete);

Parameters:-

Name Type Description
auto_delete bool Set to true if the CThreadRun instance is to
delete itself when it aborts.
Only use if the CThreadRun instance was created
using new.

Returns false if the abort command could not be set.

CThreadRun::Completed

Enquire whether the thread has completed execution. Normally this is not used; the main code will execute CThreadRun::Wait and as its name suggests, wait until the thread ends.

bool _cdecl Completed(void);

Parameters:-

None.

Returns true if the thread has completed execution.

CThreadRun::Launch

Start the thread running. The thread will invoke your version of the CThreadRun::Run function.

const int _cdecl Launch(int priority);

Parameters:-

Specify the thread priority.

Name Type Description
priority int Set to -2 for the lowest priority
Set to -1 for low priority
Set to 0 for normal
Set to 1 for high priority
Set to 2 for the highest priority

Returns one of the following self-explanatory codes:-

const int Launch_OK               = 0;
const int Launch_OutOfMem         = 1;
const int Launch_TooManyThreads   = 2;
const int Launch_Failed           = 3;

CThreadRun::ReadAbort

Read whether the instance has been instructed to abort.

bool _cdecl ReadAbort(void);

Parameters:-

None.

Returns true if this instance has been instructed to abort.

CThreadRun::ReadAutoDelete

Read whether the instance has been instructed to delete itself upon completion.

bool _cdecl ReadAutoDelete(void);

Parameters:-

None.

Returns true if this instance has been instructed to auto delete.

CThreadRun::Run

You must create a version of this function, in order to execute your thread code.

int __stdcall Run(void);

Parameters:-

None.

Returns a thread exit value which can be read by CThreadRun::Wait.

CThreadRun::SetAutoDelete

Instruct the instance to automatically delete itself when it completes execution; by default it will not.

You should only use this if the CThreadRun instance was created using new. You may only use it before executing CThreadRun::Launch.

The automatic deletion facility is useful if your main code will never need to refer to the CThreadRun instance after launch. It is also useful in the case of an abort, allowing the thread to delete itself after the abort.

void _cdecl SetAutoDelete(void);

Parameters:-

None.

Returns nothing.

CThreadRun::Wait

In your main code, use this to wait until the thread has completed.

const int _cdecl Wait(unsigned long* exitcode,int timeout_ms);

Parameters:-

Name Type Description
exitcode unsigned long* Optional.Pass a pointer to a location to receive the exit codefrom your CThreadRun::Run function, or NULL.
timeout_ms int The time to wait, in milliseconds.

Returns one of the following values:-

  • 1 if the thread has completed
  • -1 if the thread has timed out
  • 0 if another error has occurred

CThreadRun Example 1
The example below illustrates the use of the CThreadRun class.

The class below is derived from CThreadRun. It illustrates the operation of a thread, in this case simply incrementing a counter, but the private and public functions which increment and read the count are thread safe, by using the other two classes.

Note the implementation of CThreadRun::Run in the example derived class below:-

class CMyThread: public t2a::CThreadRun
{
private:
   int m_Count;

   t2a::CThreadMutex m_Mutex;

   int __stdcall Run(void)
   {
      while(GetCount() < 20)
     {
        Sleep(1000);
	//pause 1 second
         printf("%d...",m_Count);
         IncCount();
         printf("\r\n");
     }
     return(0);
   }

 //increment count, thread safely
   void IncCount(void)
   {
      t2a::CThreadLock lk(&m_Mutex);
      if(lk.Lock(30000))
      {          //we have exclusive access
         m_Count++;
         lk.Unlock();
      }
   }

public:
//constructor
   CMyThread()
   { 		//initialise the count value
      m_Count=0;
   }

    //return a value from the CThreadRun instance
   //in a thread safe manner
   int GetCount(void)
   {
      int res=-1;
      t2a::CThreadLock lk(&m_Mutex);
      if(lk.Lock(30000))
      {          //we have exclusive access
                 //so read the count
         res = m_Count;
                 //and release the lock
         lk.Unlock();
      }
      return(res);
   }
};

This is the main code that creates the CThreadRun derivative, launches the thread, and waits for completion.

CMyThread* tr=new (std::nothrow)CMyThread();
unsigned long exit_code = -1;

if(tr!=NULL)
{
   tr->Launch(-1);

   //wait for up to 60 seconds
   if(tr->Wait(&exit_code,60000)==1)
   {

      //and read a value from the thread
      int thread_count = tr->GetCount();
      //finally delete the CThreadRun derived class instance
      delete tr;
   }
   else
   {
      //timed out, so abort the CThreadRun instance
      tr->Abort(true);
      tr = NULL;
   }

}

CThreadRun Example 2

In this example, the class CMyThread2 executes a long running operation but checks periodically if it should abort. Note the data members in CMyThread2 including a std::string instance. These members are correctly destroyed.

Here is the class; see below for its use…

class CMyThread2: public t2a::CThreadRun
{
private:
   int m_Data1;
   std::string m_Text1;
public:

	//destructor
   ~CMyThread2()
   {
      printf("CMyThread2 destructor invoked...\r\n");
   }

private:

   int __stdcall Run(void)
   {
      printf("CMyThread2::Run commenced...\r\n");

      //simulate a long running thread that will be aborted

      //check if the thread is to be aborted
      while(ReadAbort()==false)
      {
         //do some operation here
      }

      printf("CMyThread2::Run aborted now,exiting...\r\n");

      return(0);
   }

public:
};

..and here is the class in use. The main code launches the thread, then waits for a short while and then aborts when the CThreadRun::Wait times out, but it tells the thread instance to auto delete. For the sample code, the main code then pauses to give the CMyThread2 instance time to abort, so that we may observe the event.

//create  CThreadRun derived class instance
CMyThread2* tr=new (std::nothrow)CMyThread2();

if(tr!=NULL)
{
   printf("main code starting thread...\r\n");
   tr->Launch(-1);

   //wait for a short while
   //(note that we do not provide a location for the exit code)

   if(tr->Wait(NULL,2000)== -1)
   {
      //the wait has timed out...

      //abort and tell the CMyThread2 instance to auto delete.
      tr->Abort(true);

      //it is good practice to remove a reference when
      //we know that the subject of that reference
      //is about to be deleted.

      //(if the instance had NOT been set to auto delete,
      //it would have been explicitly deleted using the
      //delete command).

      tr = NULL;
   }

   //pause to allow the thread to abort
   //and delete the CMyThread2 instance
   //so that we may observe in the console...
   Sleep(2000);

}

Downloads

You may download this source code from http://t2a.co/dl/thread_cpp_1.0.0.1.zip.

This code is provided on an “as is” basis, no warranty is granted or assumed. You may freely use and modify it.

Under the Hood – Launching Threads in the Windows API

There are a range of options for launching threads in the Windows API:-

  1. CreateThread.On the face of it, this seems to be the obvious means to create a thread.
  2. _beginthreadexThis function is recommended when the thread uses the Windows runtime library (the CRT); it seems that a low memory situation can cause the entire process to terminate in a CRT function called from a thread initiated by CreateThread.In my experience, this function is the safest means to initiate a thread in the Windows API, and when used with the companion _endthreadex it frees the resources and thread handle correctly. There is a similar function _beginthread which offers less functionality.

Our class CThreadRun uses _beginthreadex, except for a Windows Mobile builds (the SDK does not include it).