An Object Oriented Approach to Threading in C++

Multithreading in C++ can be a daunting topic to approach. Not only is the subject matter confusing and filled with gotchas, but it can also be hard to know how to effectively implement threads in real code and real situations. In this tutorial I will describe an object oriented approach to dealing with threads that is a solid starting point for anyone starting out and even those with some experience with threads. The design described here deals with some of the common pitfalls associated with threading and a thread class is presented that alleviates them.

The thread class detailed in this article is meant as a starting point, threading is by its very nature complex and different situations call for different solutions. With that being said I think that it is a useful and durable foundation to start from when designing with threads in mind.

As always I am using the Boost.Thread library as it is mature, well documented and the new C++0x threading features will copy it closely. However the concepts described here are universal and the thread class could be easily converted to use another library.

Some familiarity with threading concepts is assumed when reading this tutorial, but the thread class itself can be used with minimal prior knowledge.

First things first, you can get the source code for the thread class here. It is advisable to use it as a reference while reading this tutorial.

The Problem with Threading

When dealing with threads there are a few things that routinely cause headaches and consternation if not considered and dealt with properly.

Don't Pull the Rug Out

A thread MUST have valid data to work with at all times, period. If it doesn't then the program will soon enter the realm of undefined behaviour. A typical example of this becoming a problem would be a thread happily working away inside a class and then that object being deleted leaving the thread with completely invalid data to work with. What is needed here is a guarantee that the data that the thread works with will remain valid until it has finished execution.

The End of the Line

At some point in the execution of the program it will become necessary to stop a running thread and then destroy any data associated with it. This sounds simple but in practice can lead to deadlocks, crashes and other problems if not designed for properly. Moreover it needs to happen automatically, manual management of the shutdown of threads is a chore and is also error prone.

Controlling Risk

Threads are a complex programming feature and if used carelessly can lead to horribly subtle bugs that are not reproducible 99% of the time. To reduce the risk of this happening threads need to be used in carefully defined roles and with access to only the bare minimum of resources. They also need to have well defined interfaces when interacting with the rest of the program.

The Solution

The solution is to tightly couple a thread to a class object; Make the lifetime of the thread the lifetime of the object. Make the interface to the class the interface to the thread. Herb Sutter advocates a similar approach and describes the benefits in one of his articles that you can find here.

The thread class will be described in detail below but the general overview is:

  • The thread is started when the object is completely constructed.
  • The thread is stopped before the object is destroyed.
  • An interruption mechanism is used.
  • The thread is encapsulated inside the class.

Sounds simple eh? Right!
Read on for a discussion of the pertinent parts of the thread class.

Starting the Thread

The thread is started at the very end of the constructor of the object. This is important, as it guarantees that all class data has been set up before the thread starts running.


ThreadClass::ThreadClass()
{
  // this should always be the last line in the constructor
  internalThread_ = boost::thread(&ThreadClass::threadMain, this);

} // Constructor

When initialising a boost::thread a function pointer is passed for the thread to execute. As of boost 1.36.0 the boost::thread constructor uses the same syntax as Boost.Bind for convenience. Check out my tutorial on Boost.Bind if your interested in the exact syntax.

Running the Thread

The thread runs in the threadMain member function of the thread class and when the function returns the thread is finished.

Don't Let Exceptions Bring You Down

In the threadMain function it is important to note the try catch block:


void ThreadClass::threadMain()
{
  try
  {
    /* add whatever code you want the thread to execute here. */

  }
  catch (boost::thread_interrupted& interruption)
  {
    // thread was interrupted, this is expected.

  }
  catch (std::exception& e)
  {
    // an unhandled exception reached this point, this constitutes an error

  }

} // threadMain

There are two catch clauses; one for catching interruptions and the other for unhandled exceptions. Now it is extremely bad if an exception reaches the top of a thread function. On windows the entire process will be terminated and on linux you just lose the thread with only a signal being generated. Obviously this is undesirable and hence a catch clause of this type is mandatory in any thread main function. If you are wondering why catch(...) isn't used it is because there are serious issues with doing that, working out what that is is left as an exercise for the reader. Interruption of the thread will be discussed elsewhere.

Stopping the Thread

The pertinent place to look with regard to this is in the destructor:


ThreadClass::~ThreadClass()
{
  internalThread_.interrupt();
  internalThread_.join(); // make damn sure that the internal thread is gone
				                    // before we destroy the class data.
} // Destructor

So this is very straightforward; first the thread is interrupted and then it waits until the thread has finished before destroying the object. A few details are pertinent here: First join simply means “wait until the thread has finished executing” no more no less. Second no class data is destroyed until the destructor returns, the destructor function is a chance to do stuff immediately before the object is destroyed.

How Interruption Works

Communicating to a thread that it has been interrupted is not a simple process under normal circumstances, usually requiring a boolean variable for communication and must be implemented on a case by case basis. To remedy this Boost.Thread supplies an interruption mechanism as a library feature. If you remember in threadMain there was a catch clause for interruptions:


  catch (boost::thread_interrupted& interruption)
  {
    // thread was interrupted, this is expected.

  }

So the way the feature works is this. You interrupt a thread by calling it's interrupt member function, however this does not immediately interrupt the thread. Instead the next time the interrupted thread reaches an interruption point, the interruption point throws an exception of type thread_interrupted. The catch clause in thread main caches these exceptions and then allows the function to return promptly, which stops the thread. An interruption point is any one of these methods. This seems a little convoluted at first but it is better than the hand crafted alternative in my opinion. For more information on how thread interruption works check out this article.

Encapsulation

A useful property that this design possesses is that the thread is encapsulated inside the class along with the data it works with. Any interaction with the thread or it's data must happen through the public interface of the class that contains it. This helps to encourage interactions with the thread and it's data to be designed for more readily. The class defines how a client can interact with the thread and what restrictions and conditions are placed on that interaction. Encapsulation is a well known benefit of Object Oriented Design and is of great benefit when applied to threads in a similar fashion.

There You Have It

The thread class is pretty simple, but covers a few common problems that using threads can spring on the unwary. Even if you don't use this particular thread class I will bet that you will be doing something similar; using a thread inside a class is a common idiom. This basic template has stood me in good stead and I'm sure it will for you too if you give it a chance, but remember that you can modify it in whatever way you need for it to work in your particular circumstance. Threading can never have one size fits all solutions and this is an example of one specific good way to manage the use of threads in a C++ application.

FacebookTwitterGoogle+RedditDeliciousLinkedInEvernoteSlashdot

4 Responses to “An Object Oriented Approach to Threading in C++”


  1. I suggest you implement a little test program based on these ideas, and then actually compile and run it. You may find that after the internalThread_.interrupt(); call in the destructor the following join() will not be reached, leading to all sorts of interesting error messages, finally ending with a terminate() call. Has something to do with what happens to the stack during catching exceptions.
    Good luck, Notmyre

    PS There must be a reason the Boost.Thread docs do not use member functions as Callable-s. :-)

    Notmyre Alname
    October 1st, 2011
    • Notmyre, I have been using the thread class that I have outlined here for a long time without issue and the idiom I describe is widely used in the codebase where I work, powering servers and applications that have been running successfully for years.

      I think that the misconception that you have is that the internalThread_.interrupt() throws an exception, it certainly does NOT. As described above, once interrupt() has been called (which must happen from another thread) the internalThread_ continues until it reaches an interruption point and only then is an exception thrown inside the internalThread_. Meanwhile the thread in the destructor hits the join() call, blocking until the internalThread_ exits. The boost::thread_interrupted exception thrown inside internalThread_ is caught at the top of threadMain() and the thread exits happily.

      You also allude to stack unwinding but misunderstand the situation. Each thread has it’s own stack and stack unwinding therefore fundamentally cannot cross thread boundaries. So unless somehow the internalThread_ is running the destructor for the ThreadClass object (this would be all kinds of bad if true) stack unwinding will play no role for the thread executing the destructor.

      radman
      November 8th, 2011
  2. It looks like this has the potential to fit an inheritance model very well, i.e., derive classes from this which define code the thread will execute in the try block (maybe a derived pure function?)

    Like this:

    void ThreadBase::ThreadMain()
    {
      try
      {
        boost::mutex Mut;
        boost::lock_guard ProtectUserFn(Mut);
      
        ThreadDoUserExec(); //Pure virtual
      }
    }
    

    But in this case, this causes undefined behavior (a potential pure-virtual call) dependent on whether the parent thread finishes constructing before the child thread grabs it’s time-slice and follows the yet-incomplete virtual table.

    We’re calling a virtual function in a constructor, so it becomes necessary to define a method that instantiates the thread object outside.

    I’ll confess, the intermittent runtime errors about every one-hundredth build really stumped me, so I’ll post my implementation here in the hopes that I’ll help someone avoid/resolve their problem.

    The Header:

    #pragma once
    
    #include
    #include
    
    /*
    * Because of the critically sensitive nature of threads
    * additional debug-only headers are supplied to assist,
    * helping to ensure proper handling of threads on time.
    */
    
    #include
    #include
    
    #include
    #include
    
    #include “Defines.h”
    
    class ThreadBase
    {
    public:
      ThreadBase();
      virtual ~ThreadBase();
    
      void ThreadInit();
    
      virtual void ThreadDoUserExec() = 0;
      void ThreadMain();
    
      std::atomic_bool Signals[200];
    
    private:
      boost::thread Internal_thread;
    };
    

    The Implementation:

    #include “ThreadBase.h”
      
    ThreadBase::ThreadBase()
    {
      memset( (void *)Signals, NULL, sizeof(std::atomic_bool) * 200);
    }
    
    ThreadBase::~ThreadBase()
    {
      Internal_thread.interrupt();
      assert( (Internal_thread.joinable() ) );
      
      Internal_thread.join();
    }
    
    void ThreadBase::ThreadInit()
    {
      Internal_thread = boost::thread(&ThreadBase::ThreadMain, this);
    }
    
    void ThreadBase::ThreadMain()
    {
      try
      {
        boost::mutex Mut;
        boost::lock_guard ProtectUserFn(Mut);
        
        ThreadDoUserExec();
      }
      catch(boost::thread_interrupted& Interruption)
      {
        //Interrupt, etc…
      }
      catch(std::exception & STDExcept)
      {
        //Exception, etc…
      }
    }
    

    EDIT: Condensing double post and formatting : radman

    _Object
    June 5th, 2013
    • Your approach is interesting, I hadn’t seriously considered wrapping this idiom with inheritance. The fact that an init function is required is a bit awkward but there is some potential here, although I suspect the base -> derived dynamic could cause the code to become hard to understand in some circumstances.

      radman
      June 6th, 2013

Leave a Comment