Managing Threads with a Vector
Wednesday, 10 December 2008
One of the nice things about C++0x is the support for move
semantics that comes from the new Rvalue
Reference language feature. Since this is a language feature, it
means that we can easily have types that are movable but not
copyable without resorting to std::auto_ptr
-like
hackery. One such type is the new std::thread
class. A
thread of execution can only be associated with one
std::thread
object at a time, so std::thread
is not copyable, but it is movable — this
allows you to transfer ownership of a thread of execution between
objects, and return std::thread
objects from
functions. The important point for today's blog post is that it allows
you to store std::thread
objects in containers.
Move-aware containers
The C++0x standard containers are required to be move-aware, and
move objects rather than copy them when changing
their position within the container. For existing copyable types that
don't have a specific move constructor or move-assignment
operator that takes an rvalue reference this amounts to the same
thing — when a std::vector
is resized, or an
element is inserted in the middle, the elements will be copied to
their new locations. The important difference is that you can now
store types that only have a move-constructor and
move-assignment operator in the standard containers because the
objects are moved rather than copied.
This means that you can now write code like:
std::vector<std::thread> v; v.push_back(std::thread(some_function));
and it all "just works". This is good news for managing multiple
threads where the number of threads is not known until run-time
— if you're tuning the number of threads to the number of
processors, using
std::thread::hardware_concurrency()
for example. It also
means that you can then use the
std::vector<std::thread>
with the standard library
algorithms such as std::for_each
:
void do_join(std::thread& t) { t.join(); } void join_all(std::vector<std::thread>& v) { std::for_each(v.begin(),v.end(),do_join); }
If you need an extra thread because one of your threads is blocked
waiting for something, you can just use insert()
or
push_back()
to add a new thread to the
vector
. Of course you can also just move threads into or
out of the vector
by indexing the elements directly:
std::vector<std::thread> v(std::thread::hardware_concurrency()); for(unsigned i=0;i<v.size();++i) { v[i]=std::thread(do_work); }
In fact, many of the examples in my
book use std::vector<std::thread>
for managing
the threads, as it's the simplest way to do it.
Other containers work too
It's not just std::vector
that's required to be
move-aware — all the other standard containers are too. This
means you can have a std::list<std::thread>
, or a
std::deque<std::thread>
, or even a
std::map<int,std::thread>
. In fact, the whole C++0x
standard library is designed to work with move-only types such as
std::thread
.
Try it out today
Wouldn't it be nice if you could try it out today, and get used to
using containers of std::thread
objects without having to
wait for a C++0x compiler? Well, you can — the
0.6 beta of the just::thread
C++0x
Thread Library released last Friday provides a specialization of
std::vector<std::thread>
so that you can write code
like in these examples and it will work with Microsoft Visual Studio
2008. Sign up at the just::thread
Support Forum to download it today.
Posted by Anthony Williams
[/ threading /] permanent link
Tags: threads, C++0x, vector, multithreading, concurrency
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
5 Comments
thanks for sharing
Hi Anthony,
I understand that move-aware containers not copy their elements but rather move them. How does this affect the contiguous memory guarantees of std::vector<T>? Is that guarantee still provided?
Matthias
Yes, std::vector<T> still has the contiguous memory guarantee. Move semantics don't make a difference here - it just changes the operation from a copy to a move.
Thanks for the clarification, Anthony.
Thanks for the join_all method, easy to write a program with core number in arguments w/ :)