A Generic C++ Thread Class

Environment: Win32, VC++ 5.0-6.0, NT 4.0, Win95/98

Preface

I have been using code from code havens like code project and codeguru for quite some time. Its payback time folks. Well not with something that can be readily used by all, it is my sincere start.

In this article, I have tried to overcome the the lack of Object Oriented Programmability in using Worker threads. This project was more evolutionary than revolutionary. It started with the theory from Ryan Teixeira's work on Designing a Thread Class, for which I started with a basic shape. Then came the requirements for my current project for which I had to consider little/easy portability constraints.

Enough talking lets move on.

Things I wont deal here

I assume you are familiar with the concept of Threads and have heard of or used at least a worker thread. My aim is to show how quickly this class can bring you closer to OO Programming.

The Class

The CThread class in its declaration form is below. I have gone against my coding practices (which I believe is pretty much the same as most programmers) and have commented the code heavily in the source files for this purpose.
class CThread
{
public:
  /*
   *   Info: Default Constructor
   */
  CThread();

  /*
   *   Info: Plug Constructor
   */
  CThread(LPTHREAD_START_ROUTINE lpExternalRoutine);

  /*
   *   Info: Default Destructor
   */
  ~CThread();

  /*
   *   Info: Starts the thread.
   */
  DWORD Start( void* arg = NULL );

  /*
   *   Info: Stops the thread.
   */
  DWORD Stop ( bool bForceKill = false );

  /*
   *   Info: Starts the thread.
   */
  DWORD GetExitCode() const;

  /*
   *   Info: Attaches a Thread Function
   */
  void Attach( LPTHREAD_START_ROUTINE lpThreadFunc );

  /*
   *   Info: Detaches the Attached Thread Function
   */
  void  Detach( void );

protected:

  /*
   *   Info: DONT override this method.
   */
  static DWORD WINAPI EntryPoint( LPVOID pArg);

  /*
   *   Info: Override this method.
   */
  virtual DWORD Run( LPVOID arg );

  /*
   *   Info: Constructor-like function. 
   */
  virtual void ThreadCtor();

  /*
   *   Info: Destructor-like function. 
   */
  virtual void ThreadDtor();
  
private:
  /*
   *   Info: Thread Context Inner Class
   *
   *   Every thread object needs to be associated with a set 
   *   of values like UserData Pointer, Handle, Thread ID etc.
   *  
   *  NOTE: This class can be enhanced to varying functionalities
   *    eg.,
   *    * Members to hold StackSize
   *    * SECURITY_ATTRIBUTES member.
   */
  class CThreadContext
  {
  public:
    CThreadContext();

    /*
     *   Attributes Section
     */
  public:
    HANDLE m_hThread;     // The Thread Handle
    DWORD  m_dwTID;       // The Thread ID
    LPVOID m_pUserData;   // The user data pointer
    LPVOID m_pParent;     // The this pointer of the parent
                          //   CThread object
    DWORD  m_dwExitCode;  // The Exit Code of the thread
  };

  /*
   *   Attributes Section
   */
protected:
  /*
   *   Info: Members of CThread
   */
  CThreadContext  m_ThreadCtx; // The Thread Context member
  LPTHREAD_START_ROUTINE m_pThreadFunc; // The Worker Thread
                                        // Function Pointer
};

The Methods

Method Name Description
CThread() Uses the internal default EntryPoint static function as the ThreadFunc.
CThread( LPTHREAD_START_ROUTINE pThreadFunc) The plug constructor. This is used instead of creation and a call to Attach.
~CThread() Closes the thread object and destroys the thread. Can be changed if you dont want the thread to be terminated when the object destroys.
DWORD Start( LPVOID pArg = NULL ) This method starts/spawns the thread. returns any ERROR_SUCCESS if succeeded otherwise the error value as returned by GetLastError().
DWORD Stop( bool bForceKill = false ) This method returns the ExitCode if the thread has already terminated. the parameter optionally allows it to terminate the thread forcefully.
DWORD GetExitCode() const Returns the exit code of the last thread.
void Attach( LPTHREAD_START_ROUTINE lpThreadFunc )

I Admit, I borrowed this feature after knowing COM only. It is a great concept. Here, It is used to attach any Worker Thread function this makes the generic class itself readily usable. How? we will see soon.

void Detach( void ) This method removes the object from any hooked worker function to the default EntryPoint static method.
static DWORD WINAPI EntryPoint( LPVOID pArg ) This method is the only trick you need to do to get the class contain a worker thread, others are trivial.
virtual DWORD Run( LPVOID arg ) The body method, this is the method that should contain your worker thread code. Note: the signature is similar to your worker thread and that arg is the user data.
virtual void ThreadCtor() The mimic of a constructor for the life/scope of the thread to be spawned. you can override this method and write the Thread Level construction/initialization. This method will be called before the spawn/start of the thread.
virtual void ThreadDtor() The mimic of a destructor for the life/scope of the thread spawned. you can override this method and write the Thread Level Un-initialization. This method will be called after the end of the thread.

Usage

The best way to understand any code is through an example:

Scenario 1

This more like a common scenario than un-usual. Assume you have worker thread function "Threaded"

DWORD WINAPI Threaded( LPVOID lpData )
{
  while(1)
  {
    printf("worker threaded code");
    Sleep(1000);
  }
}

And now you would like to USE and CONTROL it (meaning its scope, life visibility, etc. ) in OO style. This is how you would do it:

  CThread t1; 

  t1.Attach(Threaded); // Threaded is the name of
                       //    the worker function.

  t1.Start();

  //  ... Do something useful 

  t1.Stop();

  // Alternatively

  CThread t2(Threaded);
  
  t2.Start();

  // ... Do something useful

  t2.Stop(true); // force kill if running.

There should be a catch. The catch is that you lose the CTOR and DTOR mimic functions ThreadCTOR() and ThreadDTOR() functionality and you cannot access any members or methods of the encapsulated class. But still, these were not our requirments. But what if they are...

Scenario 2

The OO Way. Even if it takes a few minutes this is the better way to do it.

You derive a class and override only the run method. That's all there is to it. You may add as many members/methods you need.

class CDemoThread : public CThread
{
  virtual DWORD Run( LPVOID arg )
  { 
    while(1)
    {
      printf("Threaded Object Code \n");
      Sleep(1000);
    }
  }
};

Now, how do you use this? Not much difference.

  CDemoThread dmt;

  dmt.Start(NULL);

  // ... do something useful

  dmt.Stop(true);

Things to come

I am planning to update this class with more stuff. If anyone is interested or has anything in particular, let me know.

Conclusion

We shall have code that is now Object Oriented. The benefits of Object Oriented Programming is way too great to realize at an early stage (Not inviting any debates).

Hope you all will find it useful !!!

Downloads

Download source & demo project - 37 Kb


Comments

  • Thanks

    Posted by liu_shu_l on 12/13/2005 08:00pm

    But , How can I Download the demo project?

    Reply
  • How do the caller know the destroy cleaness?

    Posted by Legacy on 08/24/2002 12:00am

    Originally posted by: Atrament

    Caller may return before the thread is killed cleanly while calling Stop. An exception may be raised.

    Reply
  • Fine

    Posted by Legacy on 08/09/2002 12:00am

    Originally posted by: Sancy

    this isn't bad

    tnks

    Reply
  • OO

    Posted by Legacy on 03/11/2002 12:00am

    Originally posted by: Fabio


    Actually, in my opinion, scenarion 1 is more OO than scenario 2.

    Scenario 2 is function oriented.

    Scenario 1 sees a funcion/method as a object, in wich you can stable in a stack, copy, move, delete, exchange.

    Scenario 2 requires a change in the class structure, because you have to override from CThread.

    Scen1 is more OO and more OO friendly in my opinion.

    Sorry for being picky, I really like such classes, because they bring us closer to OO.

    Reply
  • Thanks

    Posted by Legacy on 12/05/2001 12:00am

    Originally posted by: phattius

    I'm learning C++ by myself and these type of projects are very helpful. I just wanted to say thanks for spending the time to help out beginners like me. One thing i've been struggling with as of late is using threads with mfc. It seems the more i learn the more i would like to get away from mfc and this will be a great help.

    Reply
  • Here is a great alternative to doing this yourself

    Posted by Legacy on 11/24/2001 12:00am

    Originally posted by: Tyson Solberg

    The OO based thread system I have been using in my development efforts lately is call ZThreads. I think you will find that it covers just about everything you would need, and is cross platform to boot!

    check it out:
    http://www.cse.buffalo.edu/~crahen/projects/zthread/index.html

    Reply
  • Thread manager

    Posted by Legacy on 11/23/2001 12:00am

    Originally posted by: Tomaz Stih

    When you call something a thread, I expect it to encapsulate the thread. Something in the lines of:

    class Thread
    overridable method Run
    overridable method Pause
    overridable method Stop
    ...

    Your code would be called Thread manager, because it is not actually a thread, but an API wrapper, that manages worker threads.

    Regards,
    Tomaz

    Reply
  • BUG:sizeof(this)

    Posted by Legacy on 11/22/2001 12:00am

    Originally posted by: Darkay Li

    On internal class CThreadContext,you using the follow method:

    memset(this,0,sizeof(this));

    as everybody known sizeof(this)==4 not the sizeof(CThreadContext).

    and BTW:use _beginthread will be better than CreateThread, because it is C runtime thread-safe.

    Reply
  • One small problem

    Posted by Legacy on 11/22/2001 12:00am

    Originally posted by: Jean Cyr

    The use of CreateThread in method Start is ill advised as this direct system API call bypasses the C runtime and breaks certain automatic C runtime functionality such as memory leak detection when debugging. It would be more appropriate to use _beginthread or _beginthreadex.

    Reply
  • Good work

    Posted by Legacy on 11/22/2001 12:00am

    Originally posted by: Ryan Binns

    For MFC based projects, you might as well use CWinThread, since it's already there, but for non-MFC projects... Well Done!!

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • A majority of organizations are operating under the assumption that their network has already been compromised, or will be, according to a survey conducted by the SANS Institute. With many high profile breaches in 2013 occurring on endpoints, interest in improving endpoint security is top-of-mind for many information security professionals. The full results of the inaugural SANS Endpoint Security Survey are summarized in this white paper to help information security professionals track trends in endpoint …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds