One major new feature in the C++0x standard is multi-threading support. Prior to C++0x, any multi-threading support in your C++ compiler has been provided as an extension to the C++ standard, which has meant that the details of that support varies between compilers and platforms. However, with the new standard, all compilers will have to conform to the same memory model and provide the same facilities for multi-threading (though implementors are still free to provide additional extensions). What does this mean for you? It means you’ll be able to port multi-threaded code between compilers and platforms with much reduced cost. This will also reduce the number of different APIs and syntaxes you’ll have to know when writing for multiple platforms.
The core of the new thread library is the std::thread class, which manages a thread of execution, so let’s start by looking at that.
Launching Threads
You start a new thread by constructing an instance of std::thread with a function. This function is then used as the entry point for the new thread, and once that function returns, the thread is finished:
void do_work();
std::thread t(do_work);
This is just like the thread-creation APIs we’re all used to—but there’s a crucial difference: This is C++, so we’re not restricted to functions. Just like many of the algorithms in the Standard C++ Library, std::thread will accept an object of a type that implements the function call operator (operator()), as well as ordinary functions:
class do_work
{
public:
void operator()();
};
do_work dw;
std::thread t(dw);
It’s important to note that this actually copies the supplied object into the thread. If you really want to use the object you supplied (in which case, you’d better make sure that it doesn’t get destroyed before the thread finishes), you can do so by wrapping it in std::ref:
do_work dw;
std::thread t(std::ref(dw));
Most thread creation APIs allow you to pass a single parameter to your newly created thread, typically a long or a void*. std::thread allows arguments too, but you can pass any number, of (almost) any type. Yes, you read that right: any number of arguments. The constructor uses C++0x’s new variadic template facility to allow a variable number of arguments like the old … varargs syntax, but in a type-safe manner.
You can now pass objects of any copyable type as arguments to the thread function:
void do_more_work(int i,std::string s,std::vector<double> v);
std::thread
t(do_more_work,42,"hello",std::vector<double>(23,3.141));
Just as with the function object itself, the arguments are copied into the thread before the function is invoked, so if you want to pass a reference you need to wrap the argument in std::ref:
void foo(std::string&);
std::string s;
std::thread t(foo,std::ref(s));
OK, that’s enough about launching threads. What about waiting for the thread to finish? The C++ Standard calls that “joining” with the thread (after the POSIX terminology), and you do that with the join() member function:
void do_work();
std::thread t(do_work);
t.join();
If you’re not planning on joining with your thread, just destroy the thread object or call detach():
void do_work();
std::thread t(do_work);
t.detach();
Now, it’s very well launching all these threads, but if you’re going to share data you’d better protect it. The new C++ Standard Library provides facilities for that, too.