Multithreading in C++0x part 2: Starting Threads with Function Objects and Arguments
Tuesday, 17 February 2009
This is the second of a series of blog posts introducing the new C++0x thread library. If you missed the first part, it covered Starting Threads in C++0x with simple functions.
If you read part 1 of this series, then you've seen how easy it is
to start a thread in C++0x: just construct an instance of std::thread
,
passing in the function you wish to run on the new thread. Though this
is good, it would be quite limiting if new threads were constrained to
run plain functions without any arguments — all the information
needed would have to be passed via global variables, which would be
incredibly messy. Thankfully, this is not the case. Not only can you
run function objects on your new thread, as well as plain functions,
but you can pass arguments in too.
Running a function object on another thread
In keeping with the rest of the C++ standard library, you're not
limited to plain functions when starting threads — the std::thread
constructor can also be called with instances of classes that
implement the function-call operator. Let's say "hello" from our new
thread using a function object:
#include <thread> #include <iostream> class SayHello { public: void operator()() const { std::cout<<"hello"<<std::endl; } }; int main() { std::thread t((SayHello())); t.join(); }
If you're wondering about the extra parentheses around the
SayHello
constructor call, this is to avoid what's known
as C++'s most vexing parse: without the parentheses, the
declaration is taken to be a declaration of a function called
t
which takes a
pointer-to-a-function-with-no-parameters-returning-an-instance-of-SayHello
,
and which returns a std::thread
object, rather than an object called t
of type
std::thread
. There
are a few other ways to avoid the problem. Firstly, you could create a
named variable of type SayHello
and pass that to the
std::thread
constructor:
int main() { SayHello hello; std::thread t(hello); t.join(); }
Alternatively, you could use copy initialization:
int main() { std::thread t=std::thread(SayHello()); t.join(); }
And finally, if you're using a full C++0x compiler then you can use the new initialization syntax with braces instead of parentheses:
int main() { std::thread t{SayHello()}; t.join(); }
In this case, this is exactly equivalent to our first example with the double parentheses.
Anyway, enough about initialization. Whichever option you use, the
idea is the same: your function object is copied into internal storage
accessible to the new thread, and the new thread invokes your
operator()
. Your class can of course have data members
and other member functions too, and this is one way of passing data to
the thread function: pass it in as a constructor argument and store it
as a data member:
#include <thread> #include <iostream> #include <string> class Greeting { std::string message; public: explicit Greeting(std::string const& message_): message(message_) {} void operator()() const { std::cout<<message<<std::endl; } }; int main() { std::thread t(Greeting("goodbye")); t.join(); }
In this example, our message is stored as a data member in the
class, so when the Greeting
instance is copied into the
thread the message is copied too, and this example will print
"goodbye" rather than "hello".
This example also demonstrates one way of passing information in to the new thread aside from the function to call — include it as data members of the function object. If this makes sense in terms of the function object then it's ideal, otherwise we need an alternate technique.
Passing Arguments to a Thread Function
As we've just seen, one way to pass arguments in to the thread
function is to package them in a class with a function call
operator. Well, there's no need to write a special class every time;
the standard library provides an easy way to do this in the form of
std::bind
. The std::bind
function template
takes a variable number of parameters. The first is always the
function or callable object which needs the parameters, and the
remainder are the parameters to pass when calling the function. The
result is a function object that stores copies of the supplied
arguments, with a function call operator that invokes the bound
function. We could therefore use this to pass the message to write to
our new thread:
#include <thread> #include <iostream> #include <string> #include <functional> void greeting(std::string const& message) { std::cout<<message<<std::endl; } int main() { std::thread t(std::bind(greeting,"hi!")); t.join(); }
This works well, but we can actually do better than that — we
can pass the arguments directly to the std::thread
constructor and they will be copied into the internal storage for the
new thread and supplied to the thread function. We can thus write the
preceding example more simply as:
#include <thread> #include <iostream> #include <string> void greeting(std::string const& message) { std::cout<<message<<std::endl; } int main() { std::thread t(greeting,"hi!"); t.join(); }
Not only is this code simpler, it's also likely to be more
efficient as the supplied arguments can be copied directly into the
internal storage for the thread rather than first into the object
generated by std::bind
, which is then in turn copied into
the internal storage for the thread.
Multiple arguments can be supplied just by passing further
arguments to the std::thread
constructor:
#include <thread> #include <iostream> void write_sum(int x,int y) { std::cout<<x<<" + "<<y<<" = "<<(x+y)<<std::endl; } int main() { std::thread t(write_sum,123,456); t.join(); }
The std::thread
constructor is a variadic template, so it can take any number of
arguments up to the compiler's internal limit, but if you need to pass
more than a couple of parameters to your thread function then you
might like to rethink your design.
Next time
We're not done with starting threads just yet — there's a few more nuances to passing arguments which we haven't covered. In the third part of this series we'll look at passing references, and using class member functions as the thread function.
Subscribe to the RSS feed or email newsletter for this blog to be sure you don't miss the rest of the series.
Try it out
If you're using Microsoft Visual Studio 2008 or g++ 4.3 or 4.4 on
Ubuntu Linux you can try out the examples from this series using our
just::thread
implementation of the new C++0x thread library. Get your copy
today.
Multithreading in C++0x Series
Here are the posts in this series so far:
- Multithreading in C++0x Part 1: Starting Threads
- Multithreading in C++0x Part 2: Starting Threads with Function Objects and Arguments
- Multithreading in C++0x Part 3: Starting Threads with Member Functions and Reference Arguments
- Multithreading in C++0x Part 4: Protecting Shared Data
- Multithreading
in C++0x Part 5: Flexible locking
with
std::unique_lock<>
- Multithreading in C++0x part 6: Lazy initialization and double-checked locking with atomics
- Multithreading in C++0x part 7: Locking multiple mutexes without deadlock
- Multithreading in C++0x part 8: Futures, Promises and Asynchronous Function Calls
Posted by Anthony Williams
[/ threading /] permanent link
Tags: concurrency, multithreading, C++0x, thread
Stumble It! | Submit to Reddit | Submit to DZone
If you liked this post, why not subscribe to the RSS feed or Follow me on Twitter? You can also subscribe to this blog by email using the form on the left.
Design and Content Copyright © 2005-2024 Just Software Solutions Ltd. All rights reserved. | Privacy Policy
10 Comments
Hi,
How do I launch a number of threads from within a member function of an object of a class, all of which implement another member function of the same object of the same class, with possibly different values of the arguments of the second member function? For instance,
#include < iostream > #include < vector > #include < thread > #include < functional >
Class X {
public: void do_something( int n ) {}
void launch_thread() { std::vector< std::thread > threads; for ( int k = 0; k < 10; ++k ) { threads.push_back( std::thread( &X::do_something, this, k ) ); } std::for_each( threads.begin(), threads.end(), std::mem_fn( &std::thread::join ) ); } };
int main() { X an_instance; an_instance.launch_thread(); return 0; }
I am not able to get it compiled. Every time I try to compile on my Ubuntu 14.04 LTS OS having gcc compiler, I get an error saying, no matching function found at the point where the program is supposed to spawn the threads. Please help me out. Thanks !!!!
Sambuddha
Hi Sambuddha,
Your example should work with gcc on Ubuntu 14.04. Did you remember to put the -std=c++11 and -pthread flags on your command line when invoking the compiler? These are necessary when using C++11 threads with gcc. e.g.
gcc -std=c++11 -pthread test.cpp
I like this series. That one about the extra parens was - I dont know - illuminating and appalling at the same time. I have two questions: could you point to an std::thread class definition? And how do you test your code?
Hi saurabh,
The class definition for std::thread is in Chapter 30 of the latest C++0x draft: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2800.pdf
If the whole draft is a bit unwieldy, the accepted proposal for the thread library (including std::thread) is http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2447.htm
I test my code against my implementation of the C++0x thread library (http://www.stdthread.co.uk), which is available now for Microsoft Visual Studio 2008, and in pre-alpha for linux/gcc. I will be putting the documentation on the website at some point soon, which should be easier to handle than the standard text.The just::thread docs are now online at http://www.stdthread.co.uk/doc/ and the class definition for std::thread is at http://www.stdthread.co.uk/doc/headers/thread/thread.html
Is it possible to pass arguments to a class like you do with a function with std::bind?
Should have looked at the next tutorial sorry.
ABOVE ALL CODE ARE WRONG NOT COMPLING AND GIVES ERROR SUCH AS THREAD IS NOT MEMBER OF STD:: AND T NOT DECLARED IN THE FUNCTION.
Hello, the URL mentioned in the first paragraph does not work ("This is the second of a series of blog posts introducing the new C++0x thread library. If you missed the first part, it covered Starting Threads in C++0x with simple functions.") Looks like it says [...]-C++0x-[...] instead of [...]-c++0x-[...] Apparently your website works with case-sensitive URL's.
You are THE BEST.