Multithreaded C++: Part 2: Boost Threads

If pthreads represent the assembly language of multithreading programming, then boost::threads represent the C of multithreaded programming.

Boost threads introduce some handy code saving features for the creation of threads, which is nice, but not as important as the RAII techniques they put to use for mutex management. In this case an example is worth a thousand words. Here is the same code we wrote in for pthreads rewritten for boost::threads:

class threaded_class
{
public:
    threaded_class()
        : m_stoprequested(false)
    {
    }
 
    ~threaded_class()
    {
    }
 
    // Create the thread and start work
    void go() 
    {
        assert(!m_thread);
        m_thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&threaded_class::do_work, this)));
    }
 
    void stop() // Note 1
    {
        assert(m_thread);
        m_stoprequested = true;
        m_thread->join();
    }
 
    int get_fibonacci_value(int which)
    {
        boost::mutex::scoped_lock l(m_mutex); //Note 2
        return m_fibonacci_values.get(which);
    }
 
private:
    volatile bool m_stoprequested;
    boost::shared_ptr<boost::thread> m_thread;
    boost::mutex m_mutex;
 
    std::vector<int> m_fibonacci_values;
 
    int fibonacci_number(int num)
    {
        switch(num)
        {
            case 0:
            case 1:
                return 1;
            default:
                return fib(num-2) + fib(num-1);
        };
    }    
 
    // Compute and save fibonacci numbers as fast as possible
    void do_work()
    {
        int iteration = 0;
        while (!m_stoprequested)
        {
            int value = fibonacci_number(iteration);
            boost::mutex::scoped_lock l(m_mutex);
            m_fibonacci_values.push_back(value);
        }
    }                    
};

From the last pthreads example to this example our entire implementation, including comments and white space, decreased from 78 lines of code to 64. In this simple case, that was an 18% savings. Smaller, easier to read code is less error prone and easier to maintain (and therefore cheaper to write and maintain). We'll see in future articles that we can keep going with this, making the code even more succinct.

Notes regarding this version:

Note 1
In this version we still have the problem we had in the pthreads version. If we forget to call "stop" on this object we are going to at least leak thread resources and probably cause a crash.
Note 2
The boost::mutex::scoped_lock class provides a handy RAII way of managing mutex locks. In our pthread only version we were very much at risk of forgetting to unlock a mutex we had locked, causing a difficult to debug deadlock.

In this case, the length of the mutex is governed by the lifetime of the scoped_lock object. Because the scoped_lock will be destroyed as soon as it goes out of scope we know we are guaranteed to never forget to unlock the mutex.

Part 1

Comments

Pthreads in C++

Hi Jason,

Nice post.
I came to your blog through google. Can you guide me to a simple example for a threaded code in C++ ?

Reshmi

Pthreads in C++

Reshmi, the simplest meaningful example that I was able to come up with was the one I wrote for the pthreads article I have on this page: http://blog.emptycrate.com/node/270.

However, I don't recommend using pthreads in C++. It has several disadvantages, mostly in that it was written to work with C more than C++, so you must use a static or global function to launch your C++ code. The pthreads article linked above describes that in more detail.

I strongly recommend using a higher level abstraction such as boost::threads as this article describes or building or using some even higher level abstraction.

Header Files

Can you please include the header files? Saves time hunting on google. :-)

Let's see...

#include <boost/bind.hpp>
#include <boost/thread/thread.pp>
#include <boost/thread/mutex.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>

That should pretty much do it.

iteration

It might be just let night... or the coming Haloween...
Isn't the idea to increment iteration for this example to work right?

void do_work()
{
int iteration = 0;
while (!m_stoprequested)
{
int value = fibonacci_number(iteration);
boost::mutex::scoped_lock l(m_mutex);
m_fibonacci_values.push_back(value);
}
}

Thanks,

Gil

Awesome

This article has been up for months... 6000+ reads. You are the first person to notice that bug. I wish I had some kind of a prize to give you.

void do_work()
{
  int iteration = 0;
  while (!m_stoprequested)
  {
    int value = fibonacci_number(iteration);
    boost::mutex::scoped_lock l(m_mutex);
    m_fibonacci_values.push_back(value);
    ++iteration; //This should work better!
  }
} 

-Jason

fib(int) method and get_fibonacci_value(int) method

get_fibonacci_value(int) method is not used.

fib(int) is not declared.

is code complete?

thanks!

maybe fibonacci_number...

maybe fibonacci_number is a recursive method so its name should be fib()
then get_fibonacci_value should be the public method to access this "service"

Correct, fib() is a bug, it

Correct, fib() is a bug, it should be calling fibonacci_number(). Also, this is a CLASS not a complete program. You would use get_fibonacci_value as an accessor into the threaded calculation of fibonacci numbers.