C++11 - Threading Made Easy
C++11 brings rich support for threading to the language, and one of the features that
really works for me is the function template
async. This provides a great mechanism
for spinning off worker threads and easily collecting their results. In this article, I’ll show
you how I used
async to introduce threading to C++ beginners in a nice non-threatening
C/C++ and Threads
Even though the languages lacked standardized support for threading, people have been writing multithreaded C and C++ programs for quite a long time. Libraries like pthreads work pretty well and give you a shot at reasonable portability. But I found that beginners would often stumble when using these as their introduction to multithreading.
The biggest annoyance for beginners would have to be the ability to pass parameters to threads, and to return values from threads. The pthreads library handled this in a conventional way for a C API, requiring all threads to have the same function signature - which relied on void pointers, dynamic allocation, and casting. Plenty of places for the newcomer to stumble.
And returning data from a thread to the caller? You’re on your own. Ad hoc methods are easy enough to implement, but again, it’s just one more place to make a mistake.
C++11 solves most of these problems quite nicely with its thread support. In particular, for
straightforward worker thread applications, the new
async function template is
async(), a thread is modeled as a standard C++ function, as is the case
with most other libraries. However, you can pass your choice of parameters to the library - full
type safety is observed, and you can pass values by copy or reference.
The function prototype for
async is a bit of a mess, but in a nutshell, you call
async with the following arguments:
A launch policy of either
std::launch::async, which asks the runtime to create an asynchronous thread, or
std::launch::deferred, which indicates you simply want to defer the function call until a later time (lazy evaluation.) This argument is optional - if you omit it your function will use the default policy. The name of the function you are calling. The arguments to be passed to the function. Normally these are passed just as you would when calling the function, but if you wish to pass an argument by reference, you need to wrap it in a call to
The actual prototype is shown below:
You’ll note a few instances of C++11 variadic template syntax - the dot-dot-dot (not really an
ellipsis) following the word
class in the template type parameter list, and
following the argument name
Args. In both usages, this syntax means 0 or more
arguments of this type. (How these variadic arguments are processed is a topic for another post.)
The key point is that the function template
async is instantiated to accept a
typesafe list of arguments that have to match the function you are using to instantiate a thread.
My Teaching Example
To get students a feel for threading using C++11, I asked them to create a simple program
WordSearch that identifies possible Scrabble plays using a simple syntax.
A single command line argument is passed to the program - a template expression for searching
through the dictionary. I had the students identify specific characters with the actual letter to
be played, and the period character for places where any letter could be played. A typical run
might look like this:
markn@ubuntu:~ $ ./WordSearch ..x..n Found 5 matches for ..x..n sextan sexton sixain taxman taxmen markn@ubuntu:~ $
The words are identified by brute force, working through all the entries in a copy of the Scrabble dictionary. (One of the nice things about using this pattern is that I can check the program output against grep, since the periods form a regular expression.)
To get started with the program, I asked the students to simply read the words from the
Scrabble dictionary into a simple
I then asked them to create a function called
find_matches that would locate all
the matches in that
deque, and return them to the caller in a
vector<string>. A typical implementation might look like this:
The implementation of
match() should be trivial.
Calling this routine and printing the results is easy enough now:
Converting to Multithreading
At this point we have a single-threaded program. Now, thanks to the excellent work of the C++11 committee, we can convert this to a threaded implementation in two easy steps.
First, we wrap the function call in an
The return type from
async() is called a future. It doesn’t contain anything
particularly useful immediately after the function call, but after the thread exits, it will
provide a simple way to get the value returned by
find_matches, the function that
is now running as a separate thread. We can retrieve that value with a call to member function
Since the function was called with
std::launch::async, the call to
will presumably have to
join() the thread, which will block until the thread is
complete. After the thread has been joined, the future object returns the value returned by
find_matches. It really couldn’t be much easier, could it? I’m able to retrieve a
complex object as if it were returned directly from a thread, and I don’t need to write
any declarations or create any glue code to make it happen.
Using Multiple Threads
This has all been pretty easy so far, but to actually learn something about threading, it helps to
surface some of the issues that you will encounter when traveling down this path. To force the
issue, I next ask my students to modify the program so that it runs three copies of
find_matches at once.
async three times is not quite enough. I also have to make sure that I
deque<string> backlog by reference. Since all three threads are
going to be pulling data out of it simultaneously, I need to make sure that they are working on
the same copy of the data.
Because of the way C++ infers the types of arguments, it isn’t quite possible for
async to know that
backlog is a reference argument. To make that happen,
I need to include the
<functional> header and wrap reference arguments in a
ref. The result looks like this:
This fires up three threads more or less at once, and as my students are quick to see, will
inevitably result in a crash. Standard library objects like
deque are not
threadsafe - making them so would probably violate the guiding principle that you don’t pay for
things you don’t use.
The simplest solution to this problem is to use the
mutex class added to C++11, and
lock all access to the
deque. A version of
find_matches that uses a
m results in code like this:
A slight increase in complexity, but with this lock in place the program runs to completion without trouble. A run with a slightly different pattern produced the following results:
markn@ubuntu~ $ ./WordSearch ..x..l. Found 9 matches for ..x..l. in thread 1 maxilla maxwell mixable mixedly mixible saxauls sextile sixfold sixthly Found 7 matches for ..x..l. in thread 2 buxomly hexapla vexedly vexilla vixenly waxable waxbill Found 8 matches for ..x..l. in thread 3 boxball boxfuls fixable fixedly foxhole taxable taxably textile markn@ubuntu:~ $
Using process substitution under bash you can actually check that WordSearch is returning the same thing that grep sees when processing the data. For example:
./WordSearch ..x..l. 2> /dev/null | sort | diff - <(grep "^..x..l.$" sowpods.txt)
A very simple way to dip your toes in the water, courtesy of C++11.
When To Use async
In his C++11 FAQ,
Bjarne Stroustroup has this to say about
...don't even think of using async() to launch tasks that do I/O, manipulates mutexes, or in other ways interact with other tasks. The idea behind async() is the same as the idea behind the range-for statement: Provide a simple way to handle the simplest, rather common, case and leave the more complex examples to the fully general mechanism.
I’m not sure I agree with this disclaimer. The
async function is in no way a
second-class citizen when it comes to launching threads. It simply lowers the barriers to
using threads by providing a very nice mechanism for launching them and retrieving their
You can see that in my example I use a mutex to guard access to a data structure. This is not
made more dangerous by using
async to launch my thread - I use the mutex exactly
as I would if I had launched my threads using the thread class.
If your compiler supports
async, I say use it!
Does Your Compiler Support Async?
No doubt these new C++11 features are quite cool, but in order to use them, your compiler has to support them. Does yours?
It’s a little hard to read the status of
async by browsing the
Visual C++ 11 beta
charts. Regardless of what they may say, this program does build and run properly with g++ 4.7 and
the Visual C++ 11 beta.
Updating to g++ 4.7 on your Linux system can be somewhat tricky. Ideally, you would like to find a prebuilt version of the compiler that will work with your distribution. As an example, I was able to easily update my Ubuntu system with the instructions found here. The Visual Studio Community Edition is available to all for free - and installing alongside other versions of Visual C++ seems to work fine.
A little extra work maybe, but really, you should be knee-deep in C++11 by now. This is modern C++.
You can download a copy of sowpods.txt here. The full source for WordSearch.cpp is here: