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:
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.
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...
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.
-Jason
Then fix the listing please.
Then fix the listing please. I've spent 10 min trying to find out if I was not seeing the bug, checking if you were passing by reference and incrementing outside, or if the variable was global, no idea.
I don't get it why you don't fix the listing.
In any case, thanks a lot for the example. It has helped me a lot.
Julian
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.
hi, i used your concept
hi,
i used your concept using mutexes.But the thing is that my threads continously need to be running and should be stopped until and unless the program exist.So tioll that time i can not call stop function.So in order to avoid crashing wat should i do???
Auto-stopping Threads
I would put to use RAII management of my thread: (this is covered in Multithreaded C++: Part 3: RAII And Threads)
I would update the destructor of this class to automatically shutdown the thread when it is called:
Then I would place the object at the appropriate scope so that its lifetime was what I needed it to be.
I feel compelled to point out that this example is mostly academic. It's a fun way to illustrate the use of boost threads and mutexes, but it's a very inefficient way of calculating the Fibonacci sequence. A method utilising a cache or even calculating the sequence statically at compile time is a much better option.
bug
fib(0) should return 0, not 1.
Yup, you're right
I always thought it started at 1, not 0.
suggested corrections
I downloaded this and attempted to compile. These are the changes I found necessary to make it compile and work properly:
--return m_fibonacci_values.get(which);
should be:
--return m_fibonacci_values.at(which);
--int value = fibonacci_number(iteration);
should be:
--int value = fibonacci_number(iteration++);
--return fib(num-2) + fib(num-1);
should be:
--return fibonacci_number(num-2) + fibonacci_number(num-1);
What a well thought out tutorial
So useful - thanks so much.
volatile for threading? best forget about it
That volatile in your example is neither necessary nor sufficient. volatile does NOT replace proper thread synchronization mechanisms, i.e. mutexes, and if you use them, volatile isn't necessary anyway.
Using volatile in that way may actually do what you think it does on __some__ platforms, but it is certainly NOT portable.