C++ Threads Using boost::thread in 5 Minutes
This post is the first in the new "5 Minute" series. Each post is accompanied by a 5min tutorial video on YouTube. I realized when looking for YouTube tutorials that I gravitate towards the 5 minute videos rather than the longer ones. I'm usually looking for a specific technique or approach, and I've found that I can usually get what I need (or at least enough so I can Google the rest) in a short time. So here goes.
Essentials for boost::threads
Threads need:
Code Example | Description |
---|---|
#include <boost/thread.hpp> |
A header file with the necessary declarations. |
boost::thread_group tgroup; |
A container with which to manage the thread(s). |
tgroup.create_thread( boost::bind(&sing,interval) ); |
A function object to launch the thread. |
tgroup.join_all(); |
Wait for the threads to complete and clean up. |
Non-threaded Code
Below is the original version of the file that runs thing sequentially. Notice that it sings the first song, "Row, Row, Row Your Boat" completely using a free function, then it sings the second song, "I'm a Little Teapot", using a class member function. For the serial version the difference is gratuitous, but it sets the stage to demonstrate running a free function in a thread compared to running a member function in a thread.
Take notice of the calls to sing()
and teapotSinger.perform()
and their respective definitions, as these are what will run in threads in the revised code.
[crayon url="2012/05/thread_quickstart_orig.cpp" lang="cpp"/]
Threaded Code
Thread Group
The changes to thread the code are pretty small. We create a container for the threads with boost::thread_group tgroup;
. I prefer to use thread_group
unless I need specific functionality that is only available with a boost::thread
, e.g. the ability to interrupt an individual thread. The thread_group
has one really great feature: join_all()
which waits for the threads to complete in any order. When you use boost::thread
, you are responsible to shut them down and wait in the correct order: sometimes that is straightforward, and sometimes that is difficult.
Launching the Thread
To launch the thread, use boost::thread_group::create_thread()
and provide a function object as an argument. I always use boost::bind
to create the function object. (boost::bind
is the best thing since sliced bread; we'll cover it in another time.) The Boost documentation claims that the use of boost::bind
is unnecessary - that hasn't been my experience.
The first argument to boost::bind
is either a function pointer or a member function pointer, or a C++11 lambda. If it is a member function pointer, then the next argument must be a pointer to an instance of the class. Note that static
member functions are treated like functions rather than member functions. All arguments, including default arguments, must be explicitly supplied. Note that this process uses value semantics. If a type does not have value semantics, e.g. std::osteam
, then the boost::ref
library is helpful.
The thread is running when create_thread
returns. It runs from beginning to end of the function object passed to create_thread
. For clean shutdown and to allow threads time to complete their work, you must join the thread. This is where the use of thread_group
is helpful, because it provides join_all
, which waits for the threads to complete in any order and does not return until all threads have joined.
Really, getting started with threads wasn't as difficult as you thought, eh?
[crayon url="2012/05/thread_quickstart.cpp" lang="cpp"]