Blog Archive for / 2009 /
November 2009 C++ Standards Committee Mailing
Tuesday, 17 November 2009
The November 2009 mailing for the C++ Standards Committee was published last week. This is the post-meeting mailing for the October committee meeting and contains a new working draft incorporating all the proposals accepted at the October meeting.
As those of you who
read Herb
Sutter's blog
or Michael
Wong's blog will already know, the big news for concurrency is
that a proposal for std::async
has been
accepted, and this is reflected in the latest working
draft.
There are 5 concurrency-related papers (of which my name is on one), which I summarize below:
Concurrency-related papers
- N2985: C and C++ Thread Compatibility
Lawrence Crowl has been through the concurrency and multithreading support from the C++ working draft, and compared this to that now being added to the C working draft for the next C standard. This paper contains his observations and recommendations about the potential for compatibility between the types and functions provided by the two draft standards. I agree with some of his recommendations, and disagree with others. No action will be taken by the C++ committee unless his recommendations become full proposals.
- N2992: More Collected Issues with Atomics
This is Lawrence Crowl's paper which rounds up various issues with the C++ atomics. It was accepted by the committee, and has been incorporated into the working draft. The most noticeable change is that the atomic types and functions now live in the
<atomic>
header. The macro for identifying lock-free atomics has been expanded into a set of macros, one per type, and a guarantee has been added that all instances of a type are lock-free, or all are not lock-free (rather than it being allowed to vary between objects). There is also a clarification that if you use compare-exchange operations on a type with padding bits then the padding bits will be part of the comparison (which will therefore affectatomic<float>
or similar), plus a few other clarifications.- N2996: A Simple Asynchronous Call
This paper is by Herb Sutter and Lawrence Crowl, and brings together their papers from the pre-meeting mailing (N2970 and N2973) based on feedback from the committee. This is the
std::async
proposal that got accepted, and which has been incorporated into the working draft.The result is that we have a variadic
async
function with an optional launch policy as the first argument, which specifies whether the function is to be spawned on a separate thread, deferred untilget()
orwait()
is called on the returnedfuture
, or either, at the choice of the implementation.- N2997: Issues on Futures (Rev. 1)
This is a revision of N2967 from the pre-meeting mailing, which incorporates feedback from the committee. This is the version that was accepted, and which has been incorporated into the working draft. The key change is that
unique_future
has been renamed to justfuture
in order to make things easier to read in what is anticipated to be the common case.- N2999: Background for issue 887: Clocks and Condition Variables (Rev. 1)
This is a minor revision of Detlef's paper from the pre-meeting mailing (N2969). The changes it proposes have not yet been accepted into the working paper, and will make it implementation-dependent which clocks can be used for condition variable waits.
Other concurrency changes in the working draft
std::mutex
now has aconstexpr
constructor, so namespace-scope objects are guaranteed to be safe from initialization order issues.- The return type of the
wait_for
andwait_until
member functions ofstd::condition_variable
andstd::condition_variable_any
has been changed from abool
to acv_status
enum. This makes it clear whether the return is due to a signal (or spurious wake) or because the operation timed out.
Updated implementation
Our just::thread
implementation of the C++0x thread library will shortly be updated
to incorporate all these changes (including an implementation
of std::async
). Existing customers will get a free
upgrade as usual.
Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: C++0x, C++, standards, 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.
More Chances to Win a copy of just::thread in our Halloween Contest
Thursday, 29 October 2009
It's been good to see the entries coming in for our Halloween Contest, and with only two days to go we've decided to add more chances of winning. A blog post with your concurrency story now counts as TWO entries. You can get a further entry by commenting on this blog entry, and yet another by tweeting about this contest. If you do everything, that gives you four chances to win!
Not only does just::thread
provide a complete
implementation of
the C++0x standard
thread library for Windows and Linux, but it also
includes
a special
deadlock detection mode. If deadlocks are sucking the
life out of your multithreaded code then the deadlock detection
mode can get straight to the heart of the problem and tell you
which synchronization objects are the cause of the problem, which
threads are waiting and where. Crucially, the library will also
tell you which thread owns each object, and where it took
ownership.
Good luck!
Posted by Anthony Williams
[/ news /] permanent link
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.
Win a copy of just::thread in our Halloween Contest
Friday, 16 October 2009
Yes, that's right — this Halloween we've got 3 copies of
the just::thread
portability pack worth £100 to give away. The
Portability Pack provides implementations
of just::thread
for both Linux and Windows, so you can use the same code for both
platforms right off the bat.
Not only does just::thread
provide a complete
implementation of
the C++0x standard
thread library for Windows and Linux, but it also
includes
a special
deadlock detection mode. If deadlocks are sucking the
life out of your multithreaded code then the deadlock detection
mode can get straight to the heart of the problem and tell you
which synchronization objects are the cause of the problem, which
threads are waiting and where. Crucially, the library will also
tell you which thread owns each object, and where it took
ownership.
Enter this contest now for your chance to win.
How to enter
Entering is really easy — just write a short blog post about
your concurrency gremlins that links back to the contest page. It
could be that you'd like to add multithreading to your application
but don't know where to start, or you're plagued with deadlocks (in
which case you could use your new copy
of just::thread
to help you find the cause!) Maybe you've conquered all your
concurrency problems, in which case I'd like to hear about how you
did it. Whatever your story, I want to know —
even a twitter-style 140-character summary would be fine.
Once you've got your blog post up, either email contest@stdthread.co.uk, or add a comment to this blog entry. Just make sure you do it before the deadline: Halloween, 31st October 2009. For full details and terms and conditions, see the contest page.
Good luck!
Posted by Anthony Williams
[/ news /] permanent link
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.
September 2009 C++ Standards Committee Mailing
Wednesday, 30 September 2009
The September 2009 mailing for the C++ Standards Committee was published today. This is the pre-meeting mailing for the October committee meeting and contains the first new working draft since the "Concepts" feature was voted out of C++0x in July.
There is also quite a large number of papers in this mailing, including 7 concurrency-related papers (of which my name is on 2), which I summarize below:
Concurrency-related papers
- N2955: Comments on the C++ Memory Model Following a Partial Formalization Attempt
Mark has been working on a formal model for parts of the C++ memory model, and has identified a few cases where the wording could be improved to clarify things.
- N2959: Managing the lifetime of thread_local variables with contexts (Revision 1)
This is a revision of my earlier paper, N2907 which expands my proposal for a
thread_local_context
class, along with full proposed wording. I think this provides a workable solution to the general problem of ensuring that destructors forthread_local
variables are run at particular points in the execution of a program as discussed in N2880.- N2967: Issues on Futures
This paper that I co-authored with Detlef Vollmann provides a complete rewording for the "Futures" section of the standard (30.6). It folds the proposed changes from N2888 in with changes requested by the LWG at July's meeting (including the addition of a new future type —
atomic_future
which serializes all operations), and editorial comments on the earlier wording. It doesn't resolve everything — there is still some discussion over whetherunique_future::get()
should return a value or a reference, for example — but it provides a sound basis for further discussion.- N2969: Background for issue 887: Clocks and Condition Variables
Detlef argues the case that
std::condition_variable::wait_for
andstd::condition_variable::wait_until
may wake "too late" if the user adjusts the system clock during a wait, and suggests simplifying the overload set to match the minimum requirements of POSIX. LWG has already decided that this is Not A Defect (NAD), and I agree — there are no guarantees about how soon after the specified timeout the wait will return, and this is thus entirely a quality of implementation issue.- N2970: A simple async() (revision 1)
This is a revision to Herb Sutter's take on a
std::async
function. There is a lot of discussion around the various issues, but the proposed wording seems incomplete — for example, if theunique_future
associated with asynchronous
async
call is moved into ashared_future
which is then copied, which call toget()
runs the task?- N2973: An Asynchronous Call for C++
This is a revision to Lawrence Crowl's take on a
std::async
function. Whilst better specified than N2970 (a "serial" task is invoked inunique_future::get()
or when theunique_future
is moved into ashared_future
), I'm not entirely happy with the result.- N2974: An Analysis of Async and Futures
To aid the committee with deciding on the semantics of
std::async
, Lawrence has put together this paper which outlines various options for creating futures from anasync
function. He then goes on to outline the operations that we may wish to perform on those futures, and how they relate to the various options forasync
.
As you can see by the 3 papers on the issue,
std::async
is a hot topic. I hope that a single coherent
design can be agreed on at Santa Cruz, and incorporated into the
standard. Comment
on this blog post if you've got an opinion on the matter, and I'll
pass it on.
Other papers
As I said at the beginning of this post, there are quite a few papers in this mailing. Other than the new working draft, there were 3 papers that piqued my interest:
- N2954: Unified Function Syntax which proposes allowing lambda-style syntax for normal functions,
- N2953: Defining Move Special Member Functions which covers the implicit generation of move constructors and move assignment operators, and
- N2951:
forward which covers the usage and constraints on
std::forward
Other papers include changes to the allocator proposals, papers to cover specific core issues, a couple of papers on type traits and a couple on pairs and tuples. See the full paper list for further details.
Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: C++0x, C++, standards, 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.
"The Perils of Data Races" Article Online
Friday, 11 September 2009
My latest article, Avoiding the Perils of C++0x Data Races has been published at devx.com.
Race conditions are one of the biggest causes of problems in multithreaded code. This article takes a brief look at some of the ways in which race conditions can occur, with sample code that demonstrates the perils of data races.
A Data Race is a specific type of race condition, and occurs where multiple threads access the same non-atomic variable without synchronization, and at least one of those threads performs a write. The sample code in the article demonstrates how a data race can cause corrupt data, which can then cause additional problems in other code.
Posted by Anthony Williams
[/ news /] permanent link
Tags: article, multithreading, C++0x, data races, race conditions
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.
Multithreading in C++0x part 7: Locking multiple mutexes without deadlock
Friday, 21 August 2009
This is the seventh in a series of blog posts introducing the new C++0x thread library. So far we've looked at the various ways of starting threads in C++0x and protecting shared data with mutexes. See the end of this article for a full set of links to the rest of the series.
After last time's detour
into lazy
initialization, our regular programming resumes with the promised
article on std::lock()
.
Acquiring locks on multiple mutexes
In most cases, your code should only ever hold a lock on one mutex at a time. Occasionally, you might nest your locks, e.g. by calling into a subsystem that protects its internal data with a mutex whilst holding a lock on another mutex, but it's generally better to avoid holding locks on multiple mutexes at once if at all possible.
However, sometimes it is necessary to hold a lock on more than one mutex, because you need to perform an operation on two distinct items of data, each of which is protected by its own mutex. In order to perform the operation correctly, you need to hold locks on both the mutexes, otherwise another thread could change one of the values before your operation was complete. For example, consider a bank transfer between two accounts: we want the transfer to be a single action to the outside world, so we need to acquire the lock on the mutex protecting each account. You could naively implement this like so:
class account { std::mutex m; currency_value balance; public: friend void transfer(account& from,account& to, currency_value amount) { std::lock_guard<std::mutex> lock_from(from.m); std::lock_guard<std::mutex> lock_to(to.m); from.balance -= amount; to.balance += amount; } };
Though it looks right at first glance (both locks are acquired
before data is accessed), there is a potential for deadlock in this
code. Consider two threads calling transfer()
at the
same time on the same accounts, but one thread is transferring money
from account A to account B, whereas the other is transferring money
from account B to account A. If the calls to transfer()
are running concurrently, then both threads will try and lock the
mutex on their from
account. Assuming there's nothing
else happening in the system, this will succeed, as for thread 1
the from
account is account A, whereas for thread 2
the from
account is account B. Now each thread tries to
acquire the lock on the mutex for the
corresponding to
. Thread 1 will try and lock the mutex
for account B (thread 1 is transferring from A to B). Unfortunately,
it will block because thread 2 holds that lock. Meanwhile thread B
will try and lock the mutex for account A (thread 2 is transferring
from B to A). Thread 1 holds this mutex, so thread 2 must also block
— deadlock.
Avoiding deadlock
with std::lock()
In order to avoid this problem, you need to somehow tell the system
to lock the two mutexes together, so that one of the threads
acquires both locks and deadlock is avoided. This is what
the std::lock()
function is for — you supply a number of mutexes (it's a
variadic template) and the thread library ensures that when the
function returns they are all locked. Thus we can avoid the race
condition in transfer()
by writing it as follows:
void transfer(account& from,account& to, currency_value amount) { std::lock(from.m,to.m); std::lock_guard<std::mutex> lock_from(from.m,std::adopt_lock); std::lock_guard<std::mutex> lock_to(to.m,std::adopt_lock); from.balance -= amount; to.balance += amount; }
Here we
use std::lock()
to lock both mutexes safely, then adopt the ownership into
the std::lock_guard
instances to ensure the locks are released safely at the end of the
function.
Other mutex types
As mentioned
already, std::lock()
is a function template rather than a plain function. Not only does
this mean it can merrily accept any number of mutex arguments, but
it also means that it can accept any type of mutex
arguments. The arguments don't even all have to be the same
type. You can pass anything which
implements lock()
, try_lock()
and unlock()
member functions with appropriate
semantics. As you may remember
from part
5 of this series, std::unique_lock<>
provides these member functions, so you can pass an instance of std::unique_lock<>
to std::lock()
. Indeed,
you could also write transfer()
using std::unique_lock<>
like this:
void transfer(account& from,account& to, currency_value amount) { std::unique_lock<std::mutex> lock_from(from.m,std::defer_lock); std::unique_lock<std::mutex> lock_to(to.m,std::defer_lock); std::lock(lock_from,lock_to); from.balance -= amount; to.balance += amount; }
In this case, we construct
the std::unique_lock<>
instances without actually locking the mutexes, and then lock them
both together
with std::lock()
afterwards. You still get all the benefits of the deadlock avoidance,
and the same level of exception safety — which approach to use
is up to you, and depends on what else is happening in the code.
Exception safety
Since std::lock()
has to work with any mutex type you might throw at it, including
user-defined ones, it has to be able to cope with exceptions. In
particular, it has to provide sensible behaviour if a call
to lock()
or try_lock()
on one of the
supplied mutexes throws an exception. The way it does this is quite
simple: if a call to lock()
or try_lock()
on one of the supplied mutexes throws an exception
then unlock()
is called on each of the mutexes for
which this call
to std::lock()
currently owns a lock. So, if you are locking 4 mutexes and the call
has successfully acquired 2 of them, but a call
to try_lock()
on the third throws an exception then the
locks on the first two will be released by calls
to unlock()
.
The upshot of this is that
if std::lock()
completes successfully then the calling thread owns the lock on all
the supplied mutexes, but if the call exits with an exception then
from the point of view of lock ownership it will be as-if the call
was never made — any additional locks acquired have been
released again.
No silver bullet
There are many ways to write code that
deadlocks: std::lock()
only addresses the particular case of acquiring multiple mutexes
together. However, if you need to do this
then std::lock()
ensures that you don't need to worry about lock ordering issues.
If your code acquires locks on multiple mutexes,
but std::lock()
isn't applicable for your case, then you need to take care of lock
ordering issues another way. One possibility is to enforce an
ordering
by using
a hierarchical mutex. If you've got a deadlock in your code,
then things like
the deadlock
detection mode of my just::thread
library can
help you pinpoint the cause.
Next time
In the next installment we'll take a look at the "futures" mechanism from C++0x. Futures are a high level mechanism for passing a value between threads, and allow a thread to wait for a result to be available without having to manage the locks directly.
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, atomics
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.
Multithreading in C++0x part 6: Lazy initialization and double-checked locking with atomics
Thursday, 13 August 2009
This is the sixth in a series of blog posts introducing the new C++0x thread library. So far we've looked at the various ways of starting threads in C++0x and protecting shared data with mutexes. See the end of this article for a full set of links to the rest of the series.
I had intended to write about the use of the new std::lock()
for avoiding deadlock in this article. However, there was a post on
comp.std.c++ this morning about lazy initialization, and I thought I'd
write about that instead. std::lock()
can wait until next time.
Lazy Initialization
The classic lazy-initialization case is where you have an object that is expensive to construct but isn't always needed. In this case you can choose to only initialize it on first use:
class lazy_init { mutable std::unique_ptr<expensive_data> data; public: expensive_data const& get_data() const { if(!data) { data.reset(new expensive_data); } return *data; } };
However, we can't use this idiom in multi-threaded code, since
there would be a data race on the accesses to data
. Enter std::call_once()
— by using an instance of std::once_flag
to protect the initialization we can make the data race go away:
class lazy_init { mutable std::once_flag flag; mutable std::unique_ptr<expensive_data> data; void do_init() const { data.reset(new expensive_data); } public: expensive_data const& get_data() const { std::call_once(flag,&lazy_init::do_init,this); return *data; } };
Concurrent calls to get_data()
are now safe: if the
data has already been initialized they can just proceed
concurrently. If not, then all threads calling concurrently except one
will wait until the remaining thread has completed the
initialization.
Reinitialization
This is all very well if you only want to initialize the
data once. However, what if you need to update the data
— perhaps it's a cache of some rarely-changing data
that's expensive to come
by. std::call_once()
doesn't support multiple calls (hence the name). You could of course
protect the data with a mutex, as shown below:
class lazy_init_with_cache { mutable std::mutex m; mutable std::shared_ptr<const expensive_data> data; public: std::shared_ptr<const expensive_data> get_data() const { std::lock_guard<std::mutex> lk(m); if(!data) { data.reset(new expensive_data); } return data; } void invalidate_cache() { std::lock_guard<std::mutex> lk(m); data.reset(); } };
Note that in this case we return a std::shared_ptr<const
expensive_data>
rather than a reference to avoid a race
condition on the data itself — this ensures that the copy held
by the calling code will be valid (if out of date) even if another
thread calls invalidate_cache()
before the data can be
used.
This "works" in the sense that it avoids data races, but if the
updates are rare and the reads are frequent then this may cause
unnecessary serialization when multiple threads
call get_data()
concurrently. What other options do we
have?
Double-checked locking returns
Much has been written about how double-checked locking is broken when using multiple threads. However, the chief cause of the problem is that the sample code uses plain non-atomic operations to check the flag outside the mutex, so is subject to a data race. You can overcome this by careful use of the C++0x atomics, as shown in the example below:
class lazy_init_with_cache { mutable std::mutex m; mutable std::shared_ptr<const expensive_data> data; public: std::shared_ptr<const expensive_data> get_data() const { std::shared_ptr<const expensive_data> result= std::atomic_load_explicit(&data,std::memory_order_acquire); if(!result) { std::lock_guard<std::mutex> lk(m); result=data; if(!result) { result.reset(new expensive_data); std::atomic_store_explicit(&data,result,std::memory_order_release); } } return result; } void invalidate_cache() { std::lock_guard<std::mutex> lk(m); std::shared_ptr<const expensive_data> dummy; std::atomic_store_explicit(&data,dummy,std::memory_order_relaxed); } };
Note that in this case, all writes to data
use
atomic operations, even those within the mutex lock. This is
necessary in order to ensure that the atomic load operation at the
start of get_data()
actually has a coherent value to read
— there's no point doing an atomic load if the stores are not
atomic, otherwise you might atomically load some half-written
data. Also, the atomic load and store operations ensure that the
reference count on the std::shared_ptr
object is
correctly updated, so that the expensive_data
object is
correctly destroyed when the last std::shared_ptr
object
referencing it is destroyed.
If our atomic load actually returned a non-NULL
value
then we can use that, just as we did before. However, if it
returned NULL
then we need to lock the mutex and try
again. This time we can use a plain read of data
, since
the mutex is locked. If we still get NULL
then we need to
do the initialization. However, we can't just
call data.reset()
like before, since that is not
atomic. Instead we must create a local std::shared_ptr
instance with the value we want, and store the value with an atomic
store operation. We can use result
for the local value,
since we want the value in that variable anyway.
In invalidate_cache()
we must also store the value
using std::atomic_store_explicit()
, in order to ensure
that the NULL
value is correctly read
in get_data()
. Note also that we must also lock the mutex
here, in order to avoid a data race with the initialization code
inside the mutex lock in get_data()
.
Memory ordering
By using std::atomic_load_explicit()
and std::atomic_store_explicit()
we can specify the
memory
ordering requirements of the operations. We could have just
used std::atomic_load()
and std::atomic_store()
, but those would have
implied std::memory_order_seq_cst
, which is overkill in
this scenario. What we need is to ensure that if a
non-NULL
value is read in get_data()
then
the actual creation of the associated object happens-before
the read. The store in get_data()
must therefore
use std::memory_order_release
, whilst the load
uses std::memory_order_acquire
.
On the other hand, the store in invalidate_cache()
can
merrily use std::memory_order_relaxed
, since there is
no data associated with the store: if the load
in get_data()
reads NULL
then the mutex
will be locked, which will handle any necessary synchronization.
Whenever you use atomic operations, you have to make sure that the memory ordering is correct, and that there are no races. Even in such a simple case such as this it is not trivial, and I would not recommend it unless profiling has shown that this is really a problem.
Update: As if to highlight my previous point about the
trickiness of atomic operations, Dmitriy correctly points out in the
comments that the use of std::shared_ptr
to access
the expensive_data
implies a reference count, which is a
real performance suck in multithreaded code. Whatever the memory
ordering constraints we put on it, every thread doing the reading has
to update the reference count. This is thus a source of contention,
and can seriously limit scalability even if it doesn't force full
serialization. The same issues apply to multiple-reader single-writer
mutexes — Joe Duffy has written about them
over
on his blog. Time it on your platform with just a mutex (i.e. no
double-checked locking), and with the atomic operations, and use
whichever is faster. Alternatively, use a memory reclamation scheme
specially tailored for your usage.
Next time
In the next part of this series I'll cover the use
of std::lock()
that I was intending to cover in this installment.
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.
Note: since std::shared_ptr
is part of the library
supplied with the
compiler, just::thread
cannot provide the atomic functions for std::shared_ptr
used in this article.
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, atomics
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.
August 2009 C++ Standards Committee Mailing
Thursday, 06 August 2009
The August 2009 mailing for the C++ Standards Committee was published yestoday. This is the post-meeting mailing for the July committee meeting. There isn't a new working draft this time, for one very simple reason: the "Concepts" feature was voted out of C++0x, so huge sections of the draft will have to be changed. Thankfully, the concurrency-related parts had not yet had concepts applied, so will not be affected directly by this.
C++0x timetable
What does this mean for the timetable? Well, the committee currently intends to issue a new Committee Draft for voting and comments by National Bodies in March 2010. How long it takes to get from that draft to a new Standard will depend on the comments that come back, and the length of the committee's issue lists. This is not intended to be a Final Committee Draft, implying there would be at least one more round of voting and comments. As I understand it, the earliest we could get a new Standard is thus early 2011, though if that were to be the case then the details would be finalised late 2010.
Concurrency-related papers
This time, there's only one concurrency-related paper in the mailing:
- N2925: More Collected Issues with Atomics
This paper gathers together the proposed wording for a number of outstanding issues with respect to the atomic data types. This tightens up the wording regards the memory ordering guarantees of fences, and adjusts the overload set for both free functions and member functions to ensure atomics behave sensibly in more circumstances. Crucially, the header for atomic operations is also changed — rather than the <cstdatomic> / <stdatomic.h> pairing from the previous draft there is a single header: <atomic>.
Future developments
Though futures were discussed, no change was made to the working paper in this regard. Detlef and I have been asked to write a further paper on the topic to incorporate the issues raised at the meeting, which will therefore be in the next committee mailing.
Likewise, the proposals for dealing with the lifetime of
thread_local
variables and for launching an asynchronous
task with a return value are still under discussion, and further
papers on those can be expected.
Your input
How do you feel about the removal of concepts? Do you think the slip in schedule will have an impact on you? Let me know in the comments.
Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: C++0x, C++, standards, 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.
just::thread C++0x Thread Library Linux Port Released
Monday, 03 August 2009
I am pleased to announce that version 1.0 of just::thread, our C++0x Thread Library is now available for Linux as well as Windows.
The just::thread
library is a complete implementation
of the new C++0x thread library as per the current
C++0x working paper. Features include:
std::thread
for launching threads.- Mutexes and condition variables.
std::promise
,std::packaged_task
,std::unique_future
andstd::shared_future
for transferring data between threads.- Support for the new
std::chrono
time interface for sleeping and timeouts on locks and waits. - Atomic operations with
std::atomic
. - Support for
std::exception_ptr
for transferring exceptions between threads. - Special deadlock-detection mode for tracking down the call-stack leading to deadlocks, the bane of multithreaded programming.
The linux port is available for 32-bit and 64-bit Ubuntu linux, and takes full advantage of the C++0x support available from g++ 4.3. Purchase your copy and get started NOW.
Posted by Anthony Williams
[/ news /] permanent link
Tags: multithreading, concurrency, C++0x
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.
Multithreading in C++0x part 5: Flexible locking with std::unique_lock<>
Wednesday, 15 July 2009
This is the fifth in a series of blog posts introducing the new C++0x thread library. So far we've looked at the various ways of starting threads in C++0x and protecting shared data with mutexes. See the end of this article for a full set of links to the rest of the series.
In the previous installment we looked at the use of std::lock_guard<>
to simplify the locking and unlocking of a mutex and provide exception
safety. This time we're going to look at the std::lock_guard<>
's
companion class template std::unique_lock<>
. At
the most basic level you use it like std::lock_guard<>
— pass a mutex to the constructor to acquire a lock, and the
mutex is unlocked in the destructor — but if that's all you're
doing then you really ought to use std::lock_guard<>
instead. The benefit to using std::unique_lock<>
comes from two things:
- you can transfer ownership of the lock between instances, and
- the
std::unique_lock<>
object does not have to own the lock on the mutex it is associated with.
Let's take a look at each of these in turn, starting with transferring ownership.
Transferring ownership of a mutex lock
between std::unique_lock<>
instances
There are several consequences to being able to transfer ownership
of a mutex lock
between std::unique_lock<>
instances: you can return a lock from a function, you can store
locks in standard containers, and so forth.
For example, you can write a simple function that acquires a lock on an internal mutex:
std::unique_lock<std::mutex> acquire_lock() { static std::mutex m; return std::unique_lock<std::mutex>(m); }
The ability to transfer lock ownership between instances also provides an easy way to write classes that are themselves movable, but hold a lock internally, such as the following:
class data_to_protect { public: void some_operation(); void other_operation(); }; class data_handle { private: data_to_protect* ptr; std::unique_lock<std::mutex> lk; friend data_handle lock_data(); data_handle(data_to_protect* ptr_,std::unique_lock<std::mutex> lk_): ptr(ptr_),lk(lk_) {} public: data_handle(data_handle && other): ptr(other.ptr),lk(std::move(other.lk)) {} data_handle& operator=(data_handle && other) { if(&other != this) { ptr=other.ptr; lk=std::move(other.lk); other.ptr=0; } return *this; } void do_op() { ptr->some_operation(); } void do_other_op() { ptr->other_operation(); } }; data_handle lock_data() { static std::mutex m; static data_to_protect the_data; std::unique_lock<std::mutex> lk(m); return data_handle(&the_data,std::move(lk)); } int main() { data_handle dh=lock_data(); // lock acquired dh.do_op(); // lock still held dh.do_other_op(); // lock still held data_handle dh2; dh2=std::move(dh); // transfer lock to other handle dh2.do_op(); // lock still held } // lock released
In this case, the function lock_data()
acquires a lock
on the mutex used to protect the data, and then transfers that along
with a pointer to the data into the data_handle
. This
lock is then held by the data_handle
until the handle
is destroyed, allowing multiple operations to be done on the data
without the lock being released. Because
the std::unique_lock<>
is movable, it is easy to make data_handle
movable too,
which is necessary to return it from lock_data
.
Though the ability to transfer ownership between instances is
useful, it is by no means as useful as the simple ability to be able
to manage the ownership of the lock separately from the lifetime of
the std::unique_lock<>
instance.
Explicit locking and unlocking a mutex with
a std::unique_lock<>
As we saw
in part
4 of this
series, std::lock_guard<>
is very strict on lock ownership — it owns the lock from
construction to destruction, with no room for
manoeuvre. std::unique_lock<>
is rather lax in comparison. As well as acquiring a lock in the
constructor as
for std::lock_guard<>
,
you can:
- construct an instance without an associated mutex at all (with the default constructor);
- construct an instance with an associated mutex, but leave the mutex unlocked (with the deferred-locking constructor);
- construct an instance that tries to lock a mutex, but leaves it unlocked if the lock failed (with the try-lock constructor);
- if you have a mutex that supports locking with a timeout (such
as
std::timed_mutex
) then you can construct an instance that tries to acquire a lock for either a specified time period or until a specified point in time, and leaves the mutex unlocked if the timeout is reached; - lock the associated mutex if
the
std::unique_lock<>
instance doesn't currently own the lock (with thelock()
member function); - try and acquire lock the associated mutex if
the
std::unique_lock<>
instance doesn't currently own the lock (possibly with a timeout, if the mutex supports it) (with thetry_lock()
,try_lock_for()
andtry_lock_until()
member functions); - unlock the associated mutex if
the
std::unique_lock<>
does currently own the lock (with theunlock()
member function); - check whether the instance owns the lock (by calling
the
owns_lock()
member function; - release the association of the instance with the mutex, leaving
the mutex in whatever state it is currently (locked or unlocked)
(with
the
release()
member function); and - transfer ownership between instances, as described above.
As you can
see, std::unique_lock<>
is quite flexible: it gives you complete control over the underlying
mutex, and actually meets all the requirements for
a Lockable object itself. You can thus have
a std::unique_lock<std::unique_lock<std::mutex>>
if you really want to! However, even with all this flexibility it
still gives you exception safety: if the lock is held when the
object is destroyed, it is released in the destructor.
std::unique_lock<>
and condition variables
One place where the flexibility
of std::unique_lock<>
is used is
with std::condition_variable
. std::condition_variable
provides an implementation of a condition variable, which
allows a thread to wait until it has been notified that a certain
condition is true. When waiting you must pass in
a std::unique_lock<>
instance that owns a lock on the mutex protecting the data related to
the condition. The condition variable uses the flexibility
of std::unique_lock<>
to unlock the mutex whilst it is waiting, and then lock it again
before returning to the caller. This enables other threads to access
the protected data whilst the thread is blocked. I will expand upon
this in a later part of the series.
Other uses for flexible locking
The key benefit of the flexible locking is that the lifetime of the lock object is independent from the time over which the lock is held. This means that you can unlock the mutex before the end of a function is reached if certain conditions are met, or unlock it whilst a time-consuming operation is performed (such as waiting on a condition variable as described above) and then lock the mutex again once the time-consuming operation is complete. Both these choices are embodiments of the common advice to hold a lock for the minimum length of time possible without sacrificing exception safety when the lock is held, and without having to write convoluted code to get the lifetime of the lock object to match the time for which the lock is required.
For example, in the following code snippet the mutex is unlocked
across the time-consuming load_strings()
operation,
even though it must be held either side to access
the strings_to_process
variable:
std::mutex m; std::vector<std::string> strings_to_process; void update_strings() { std::unique_lock<std::mutex> lk(m); if(strings_to_process.empty()) { lk.unlock(); std::vector<std::string> local_strings=load_strings(); lk.lock(); strings_to_process.insert(strings_to_process.end(), local_strings.begin(),local_strings.end()); } }
Next time
Next time we'll look at the use of the new std::lock()
and std::try_lock()
function
templates to avoid deadlock when acquiring locks on multiple mutexes.
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.
June 2009 C++ Standards Committee Mailing - New Working Draft and Concurrency Papers
Wednesday, 24 June 2009
The June 2009 mailing for the C++ Standards Committee was published today. This is the pre-meeting mailing for the upcoming committee meeting. Not only does it include the current C++0x working draft, but there are 39 other papers, of which 6 are concurrency related.
Concurrency-related papers
- N2880: C++ object lifetime interactions with the threads API
This is a repeat of the same paper from the May 2009 mailing. It is referenced by several of the other concurrency papers.
- N2888: Moving Futures - Proposed Wording for UK comments 335, 336, 337 and 338
This is the first of my papers in this mailing. The current working draft restricts
std::unique_future
to beMoveConstructible
, but notMoveAssignable
. It also restrictsstd::shared_future
in a similar way, by making itCopyConstructible
, but notCopyAssignable
. This severely limits the potential use of such futures, so the UK panel filed comments on CD1 such as UK comment 335 which requested relaxation of these unnecessary restrictions. This paper to provide a detailed rationale for these changes, along with proposed wording.- N2889: An Asynchronous Call for C++
This is the first of two papers in the mailing that proposes a
std::async()
function for starting a task possibly asynchronously and returning a future for the result from that task. This addresses a need identified by UK comment 329 on CD1 for a simple way of starting a task with a return value asynchronously.- N2901: A simple async()
This is the second of the papers in the mailing that proposes a
std::async()
function for starting a task possibly asynchronously and returning a future for the result from that task.The primary difference between the papers is the type of the returned future. N2889 proposes a new type of future (
joining_future
), whereas N2901 proposes usingstd::unique_future
. There are also a few semantic differences surrounding whether tasks that run asynchronously aways do so on a new thread, or whether they may run on a thread that is reused for multiple tasks. Both papers provide a means for the caller to specify synchronous execution of tasks, or to allow the implementation to decide between synchronous execution and asynchronous execution on a case-by-case basis. N2901 also explicitly relies on the use of lambda functions orstd::bind
to bind parameters to a call, whereas N2889 supports specifying function arguments as additional parameters, as per thestd::thread
constructor (see my introduction to starting threads with arguments in C++0x).Personally, I prefer the use of
std::unique_future
proposed in N2901, but I rather like the argument-passing semantics of N2889. I also think that thethread_local
issues can be addressed by my proposal for that (N2907, see below). A final concern that I have is that calling the task insidefuture::get()
can yield surprising behaviour, as futures can be passed across threads, so this may end up being called on another thread altogether. For synchronous execution, I would prefer invoking the task inside thestd::async
call itself, but delaying it to theget()
does allow for work-stealing thread pools.- N2907: Managing the lifetime of thread_local variables with contexts
This is the second of my papers in this mailing. It proposes a potential solution to the lifetime-of-
thread_local
-variables issues from N2880 discussed in last month's blog post.The idea is that you manage the lifetime of
thread_local
variables by constructing an instance of a new classstd::thread_local_context
. When thestd::thread_local_context
instance is destroyed, allthread_local
variables created in that context are also destroyed. You can then construct a subsequent instance ofstd::thread_local_context
, and create newthread_local
instances in that new context. This means that you can reuse a thread for multiple unrelated tasks, without "spilling"thread_local
variables from an earlier task into later tasks. It can also be used with astd::async()
function to ensure that thethread_local
variables are destroyed before the associated future is made ready.- N2917: N2880 Distilled, and a New Issue With Function Statics
This is Herb Sutter's take on the issues from N2880. He starts with a general discussion of the issue with detached threads and static destruction of globals, and then continues with a discussion of the issues surrounding the destruction of function-scoped
thread_local
variables. In particular, Herb focuses on something he calls Functionthread_local
statics poisoningthread_local
destructors — if the destructor of athread_local
objectx
calls a function that itself uses a function-scopethread_local
y
, then the destructor ofy
might already have been called, resulting in undefined behaviour.I found Herb's coverage of the issues surrounding detached threads dismissive of the idea that people could correctly write manual synchronization (e.g. using a
std::condition_variable
or astd::unique_future
), even though this is common practice amongst those using POSIX threads (for example, in David Butenhof's Programming with POSIX Threads, he says "pthread_join is a convenience, not a rule", and describes examples using detached threads and condition variables to signal when the thread is done). I can see many possibilities for such usage, so as a consequence, I am personally in favour of his "solution" 1D: leave things as they are with regard to detached threads — it is already undefined behaviour to access something after its destruction.However, the issue Herb raises with regard to order of destruction for
thread_local
variables is important, and not something that mystd::thread_local_context
proposal addresses. As Herb points out, the problem does exist with regard to function-localstatic
variables already —thread_local
just amplifies the problem. I am inclined to go with what POSIX threads does, and whatboost::thread_specific_ptr
does: make them "phoenix" variables that are re-initialized when accessed after destruction, and are thus added back onto the destructor list. This is Herb's solution 2B.
Other papers
Now that CD1 is out, and the committee is trying to get things pinned down for CD2, Concepts are getting a lot of discussion. There are therefore several papers on Concepts in the mailing. There are also papers on automatically defining move constructors, revised wording for lambdas, a proposal for unifying the function syntax, and several others. Check out the full list of papers for details.
Your comments
Do you have any comments on the papers (especially the concurrency
ones, but if you have any comments on the others I'd like to know
too)? Which std::async
proposal do you prefer, or do you
like aspects of both or neither? Do you think that
thread_local_context
objects combined with resurrecting
thread_local
objects on post-destruction access solves
the thread_local
issues?
Let me know by posting a comment.
Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: C++0x, C++, standards, 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.
Importing an Existing Windows XP Installation into VirtualBox
Wednesday, 03 June 2009
On Monday morning one of the laptops I use for developing software died. Not a complete "nothing happens when I turn it on" kind of death — it still runs the POST checks OK — but it won't rebooted of its own accord whilst compiling some code and now no longer boots into Windows (no boot device, apparently). Now, I really didn't fancy having to install everything from scratch, and I've become quite a big fan of VirtualBox recently, so I thought I'd import it into VirtualBox. How hard could it be? The answer, I discovered, was "quite hard".
Since it seems that several other people have tried to import an existing Windows XP installation into VirtualBox and had problems doing so, I thought I'd document what I did for the benefits of anyone who is foolish enough to try this in the future.
Step 1: Clone the Disk into VirtualBox
The first thing to do is clone the disk into VirtualBox. I have a handy laptop disk caddy lying around in my office which enables you to convert a laptop hard disk into an external USB drive, so I took the disk out of the laptop and put it in that. I connected the drive to my linux box, and mounted the partition. A quick look round seemed to indicate that the partition was in working order and the data intact. So far so good. I unmounted the partition again, in preparation for cloning the disk.
I started VirtualBox and created a new virtual machine with a virtual disk the same size as the physical disk. I then booted the VM with the System Rescue CD that I use for partitioning and disk backups. You might prefer to use another disk cloning tool.
Once the VM was up and running, I connected the USB drive to the VM using VirtualBox's USB support and cloned the physical disk onto the virtual one. This took a long time, considering it was only a 30Gb disk. Someone will probably tell me that there are quicker ways of doing this, but it worked, and I hope I don't have to do it again.
Step 2: Try (and fail) to boot Windows
Once the clone was complete, I disconnected the USB drive and unmapped the System Rescue CD and rebooted the VM. Windows started to boot, but would hang on the splash screen. If you're trying this and Windows now boots in your VM, be very glad.
Booting into safe mode showed that the hang occurred after loading "mup.sys". It seems lots of people have had this problem, and mup.sys is not the problem — the problem is that the hardware drivers configured for the existing Windows installation don't match the VirtualBox emulated hardware in some drastic fashion. This is not surprising if you think about it. Anyway, like I said, lots of people have had this problem, and there are lots of suggested ways of fixing it, like booting with the Windows Recovery Console and adjusting which drivers are loaded, using the "repair" version of the registry and so forth. I tried most of them, and none worked. However, there was one suggestion that seemed worth following through, and it was a variant of this that I finally got working.
Step 3: Install Windows into a new VM
The suggestion that I finally got working was to install Windows on a new VM and swipe the SYSTEM registry hive from there. This registry hive contains all the information about your hardware that Windows needs to boot, so if you can get Windows booting in a VM then you can use the corresponding SYSTEM registry hive to boot the recovered installation. At least that's the theory; in practice it needs a bit of hackery to make it work.
Anyway, I installed Windows into the new VM, closed it down rebooted it with the System Rescue CD to retrieve the SYSTEM registry hive: C:\Windows\System32\config\SYSTEM. You cannot access this file when the system is running. I then booted my original VM with the System Rescue CD and copied the registry hive over, making sure I had a backup of the original. If you're doing this yourself don't change the hive on your original VM yet.
The system now booted into Windows. Well, almost — it booted up, but then displayed an LSASS message about being unable to update the password and rebooted. This cycle repeats ad infinitum, even in Safe Mode. So far not so good.
Step 4: Patching the SYSKEY
In practice, Windows installations have what's called a SYSKEY in order to prevent unauthorized people logging on to the system. This is a checksum of some variety which is spread across the SAM registry hive (which contains the user details), the SYSTEM hive and the SECURITY hive. If the SYSKEY is wrong, the system will boot up, but then display the message I was getting about LSASS being unable to update the password and reboot. In theory, you should be able to update all three registry hives together, but then all your user accounts get replaced, and I didn't fancy trying to get everything set up right again. This is where the hackery comes in, and where I am thankful to two people: firstly Clark from http://www.beginningtoseethelight.org/ who wrote an informative article on the Windows Security Accounts Manager which explains how the SYSKEY is stored in the registry hives, and secondly Petter Nordahl-Hagen who provides a boot disk for offline Windows password and registry editing.
According to the article on the Windows Security Manager, the portion of the SYSKEY store in the SYSTEM hive is stored as class key values on a few registry keys. Class key values are hidden from normal registry accesses, but Petter Nordahl-Hagen's registry editor can show them to you. So, I restored the original SYSTEM hive (I was glad I made a backup) and booted the VM from Petter's boot disk and looked at the class key values on the ControlSet001\Control\Lsa\Data, ControlSet001\Control\Lsa\GBG, ControlSet001\Control\Lsa\JD and ControlSet001\Control\Lsa\Skew1 keys from the SYSTEM hive. I noted these down for later. The values are all 16 bytes long: the ASCII values for 8 hex digits with null bytes between.
This is where the hackery comes in — I loaded the new SYSTEM hive (from the working Windows VM) into a hex editor and searched for the GBG key. The text should appear in a few places — one for the subkey of ControlSet001, one for the subkey of ControlSet002, and so forth. A few bytes after one of the occurrences you should see a sequence of 16 bytes that looks similar to the codes you wrote down: ASCII values for hex digits separated by spaces. Make a note of the original sequence and replace it with the GBG class key value from the working VM. Do the same for the Data, JD and Skew1 values. Near the Data values you should also see the same hex digit sequence without the separating null bytes. Replace that too. Now look at the values in the file near to where the registry key names occur to see if there are any other occurrences of the original hex digit sequences and replace these with the new values as well.
Save the patched SYSTEM registry hive and copy it into the VM being recovered.
Now for the moment of truth: boot the VM. If you've patched all the values correctly then it will boot into Windows. If not, then you'll get the LSASS message again. In this case, try booting into the "Last Known Good Configuration". This might work if you missed one of the occurrences of the original values. If it still doesn't work, load the hive back into your hex editor and have another go.
Step 5: Activate Windows and Install VirtualBox Guest Additions
Once Windows has booted successfully, it will update the SYSKEY entries across the remaining ControlSetXXX entries, so you don't need to worry if you missed some values. You'll need to re-activate Windows XP due to the huge change in hardware, but this should be relatively painless — if you enable a network adapter in the VM configuration then Windows can access the internet through your host's connection seamlessly. Once that's done you can proceed with install the VirtualBox guest additions to make it easier to work with the VM — mouse pointer integration, sensible screen resolutions, shared folders and so forth.
Was it quicker than installing everything from scratch? Possibly: I had a lot of software installed. It was certainly a lot more touch-and-go, and it was a bit scary patching the registry hive in a hex editor. It was quite fun though, and it felt good to get it working.
Posted by Anthony Williams
[/ general /] permanent link
Tags: virtualbox
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.
May 2009 C++ Standards Committee Mailing - Object Lifetimes and Threads
Monday, 18 May 2009
The May 2009 mailing for the C++ Standards Committee was published a couple of weeks ago. This is a minimal mailing between meetings, and only has a small number of papers.
The primary reason I'm mentioning it is because one of the papers
is concurrency related: N2880:
C++ object lifetime interactions with the threads API by
Hans-J. Boehm and Lawrence Crowl. Hans and Lawrence are concerned
about the implications of thread_local
objects with destructors, and
how you can safely clean up threads if you don't call join()
. The
issue arose during discussion of the proposed async()
function, but is generally applicable.
thread_local
variables and detached threads
Suppose you run a function on a thread for which you want the
return value. You might be tempted to use std::packaged_task<>
and std::unique_future<>
for this; after all it's almost exactly what they're designed for:
#include <thread> #include <future> #include <iostream> int find_the_answer_to_LtUaE(); std::unique_future<int> start_deep_thought() { std::packaged_task<int()> task(find_the_answer_to_LtUaE); std::unique_future<int> future=task.get_future(); std::thread t(std::move(task)); t.detach(); return future; } int main() { std::unique_future<int> the_answer=start_deep_thought(); do_stuff(); std::cout<<"The answer="<<the_answer.get()<<std::endl; }
The call to get()
will wait for the task to finish, but not the
thread. If there are no thread_local
variable
this is not a problem — the thread is detached so the library
will clean up the resources assigned to it automatically. If there
are thread_local
variables (used in
find_the_answer_to_LtUaE()
for example), then this
does cause a problem, because their destructors are not
guaranteed to complete when get()
returns. Consequently, the program may exit before the destructors of
the thread_local
variables have completed, and we have a
race condition.
This race condition is particularly problematic if the thread
accesses any objects of static storage duration, such as global
variables. The program is exiting, so these are being destroyed; if
the thread_local
destructors in the still-running thread
access global variables that have already been destroyed then your
program has undefined behaviour.
This isn't the only problem that Hans and Lawrence discuss —
they also discuss problems with thread_local
and threads
that are reused for multiple tasks — but I think it's the most
important issue.
Solutions?
None of the solutions proposed in the paper are ideal. I
particularly dislike the proposed removal of the detach()
member function from std::thread
. If
you can't detach a thread directly then it makes functions like
start_deep_thought()
much harder to write, and people
will find ways to simulate detached threads another way. Of the
options presented, my preferred choice is to allow registration of a
thread termination handler which is run after all
thread_local
objects have been destroyed. This handler
can then be used to set the value on a future or notify a condition
variable. However, it would make start_deep_thought()
more complicated, as std::packaged_task<>
wouldn't automatically make use of this mechanism unless it was
extended to do so — if it did this every time then it would make
it unusable in other contexts.
If anyone has any suggestions on how to handle the issue, please leave them in the comments below and I'll pass them on to the rest of the committee.
Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: C++0x, C++, standards, 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.
"Introduction to Variadic Templates in C++0x" Article Online
Thursday, 07 May 2009
My latest article, Introduction to Variadic Templates in C++0x has been published at devx.com.
This article introduces the syntax for declaring and using variadic templates, along with some simple examples of variadic function templates and variadic class templates.
Posted by Anthony Williams
[/ news /] permanent link
Tags: article, variadic templates, C++0x
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.
Designing Multithreaded Applications with C++0x: ACCU 2009 Slides
Tuesday, 28 April 2009
The ACCU 2009 conference has now finished, and life is getting back to normal. My presentation on "Designing Multithreaded Programs with C++0x" was well attended and I had a few people come up to me afterwards to say they enjoyed it, which is always nice.
Anyway, the purpose of this post is to say that the slides
are now up. I've also posted the sample
code for the Concurrent Queue and Numerical Integration
demonstrations that I did using our just::thread
implementation of the C++0x thread library.
Posted by Anthony Williams
[/ news /] permanent link
Tags: concurrency, threading, accu, C++0x
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.
just::thread discount for ACCU 2009
Monday, 20 April 2009
As I mentioned back in January, I will be speaking on "Designing Multithreaded Applications with C++0x" at ACCU 2009 on Thursday.
To coincide with my presentation, our C++0x thread library, just::thread
is
available at a 25%
discount until the 4th May 2009. just::thread
provides implementations of the C++0x thread library facilities such
as std::thread,
std::mutex,
std::unique_future<>
and std::atomic<>. The
current release works with Microsoft Visual Studio 2008, and gcc/linux
support will be available soon — it is currently undergoing
alpha testing.
Posted by Anthony Williams
[/ news /] permanent link
Tags: concurrency, threading, accu, C++0x, just::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.
Multithreading in C++0x part 4: Protecting Shared Data
Saturday, 04 April 2009
This is the fourth of a series of blog posts introducing the new C++0x thread library. The first three parts covered starting threads in C++0x with simple functions, starting threads with function objects and additional arguments, and starting threads with member functions and reference arguments.
If you've read the previous parts of the series then you should be comfortable with starting threads to perform tasks "in the background", and waiting for them to finish. You can accomplish a lot of useful work like this, passing in the data to be accessed as parameters to the thread function, and then retrieving the result when the thread has completed. However, this won't do if you need to communicate between the threads whilst they are running — accessing shared memory concurrently from multiple threads causes undefined behaviour if either thread modifies the data. What you need here is some way of ensuring that the accesses are mutually exlusive, so only one thread can access the shared data at a time.
Mutual Exclusion with std::mutex
Mutexes are conceptually simple. A mutex is either "locked" or
"unlocked", and threads try and lock the mutex when they wish to
access some protected data. If the mutex is already locked then any
other threads that try and lock the mutex will have to wait. Once the
thread is done with the protected data it unlocks the mutex, and
another thread can lock the mutex. If you make sure that threads
always lock a particular mutex before accessing a particular piece of
shared data then other threads are excluded from accessing the data
until as long as another thread has locked the mutex. This prevents
concurrent access from multiple threads, and avoids the undefined
behaviour of data races. The simplest mutex provided by C++0x is
std::mutex
.
Now, whilst std::mutex
has member functions for explicitly locking and unlocking, by far the
most common use case in C++ is where the mutex needs to be locked for
a specific region of code. This is where the std::lock_guard<>
template comes in handy by providing for exactly this scenario. The
constructor locks the mutex, and the destructor unlocks the mutex, so
to lock a mutex for the duration of a block of code, just construct a
std::lock_guard<>
object as a local variable at the start of the block. For example, to
protect a shared counter you can use std::lock_guard<>
to ensure that the mutex is locked for either an increment or a query
operation, as in the following example:
std::mutex m; unsigned counter=0; unsigned increment() { std::lock_guard<std::mutex> lk(m); return ++counter; } unsigned query() { std::lock_guard<std::mutex> lk(m); return counter; }
This ensures that access to counter
is
serialized — if more than one thread calls
query()
concurrently then all but one will block until
the first has exited the function, and the remaining threads will then
have to take turns. Likewise, if more than one thread calls
increment()
concurrently then all but one will
block. Since both functions lock the same mutex, if one
thread calls query()
and another calls
increment()
at the same time then one or other will have
to block. This mutual exclusion is the whole point of a
mutex.
Exception Safety and Mutexes
Using std::lock_guard<>
to lock the mutex has additional benefits over manually locking and
unlocking when it comes to exception safety. With manual locking, you
have to ensure that the mutex is unlocked correctly on every exit path
from the region where you need the mutex locked, including when
the region exits due to an exception. Suppose for a moment that
instead of protecting access to a simple integer counter we were
protecting access to a std::string
, and appending parts
on the end. Appending to a string might have to allocate memory, and
thus might throw an exception if the memory cannot be
allocated. With std::lock_guard<>
this still isn't a problem — if an exception is thrown, the
mutex is still unlocked. To get the same behaviour with manual locking
we have to use a catch
block, as shown below:
std::mutex m; std::string s; void append_with_lock_guard(std::string const& extra) { std::lock_guard<std::mutex> lk(m); s+=extra; } void append_with_manual_lock(std::string const& extra) { m.lock(); try { s+=extra; m.unlock(); } catch(...) { m.unlock(); throw; } }
If you had to do this for every function which might throw an exception it would quickly get unwieldy. Of course, you still need to ensure that the code is exception-safe in general — it's no use automatically unlocking the mutex if the protected data is left in a state of disarray.
Next time
Next time we'll take a look at the std::unique_lock<>
template, which provides more options than std::lock_guard<>
.
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.
March 2009 C++ Standards Committee Mailing - New C++0x Working Paper, Concurrency Changes
Monday, 30 March 2009
The March 2009 mailing for the C++ Standards Committee was published last week. This mailing contains the results of the first round of National Body voting on the C++0x draft, as well as the latest version of the C++0x working draft. This latest draft includes some changes in response to these NB comments, as agreed at the committee meeting at the beginning of March. Some of the changes related to concurrency and the thread library are listed below. The state of all comments (accepted, rejected, or unprocessed) can be found in N2863: C++ CD1 comment status.
The committee is intending to address all the comments (which may include rejecting some, as has already happened) in time to publish a second draft for National Body comments by the end of the year. If there is sufficient consensus on that draft, it will become the C++0x standard, otherwise it will have to undergo another round of revisions.
Concurrency-related Changes
The atomics library has only seen one accepted change so far, and that's a result of US issue 91: a failed compare_exchange operation is only atomic load rather than a read-modify-write operation. This should not have any impact on code that uses atomics, but can enable the implementation to be optimized on some architectures. The details can be seen in LWG issue 1043.
On the other hand, the thread library has seen a couple of accepted changes which will have user-visible consequences. These are:
std::thread
destructor callsstd::terminate()
instead ofdetach()
- Hans Boehm's paper N2082:
A plea to reconsider detach-on-destruction for thread objects, was
reviewed as part of US issue 97. The result is that if you do not
explicitly call
join()
ordetach()
on yourstd::thread
objects before they are destroyed then the library will callstd::terminate()
. This is to ensure that there are no unintentional "dangling threads" with references to local variables. std::thread
andstd::unique_lock
no longer haveswap()
functions that operate on rvalues- This change is in response to US issue 46, and the associated
paper N2844:
Fixing a Safety Problem with Rvalue References: Proposed Wording
(Revision 1), which changes the way the rvalue-references work. In
particular, an rvalue-reference no longer binds to an lvalue. Combined
with the previous change to disallow destroying
std::thread
objects with an associated thread of execution this makes perfect sense: swapping two rvaluestd::thread
objects serves no purpose anyway, and swapping astd::thread
variable with an rvalue would now callstd::terminate()
when the rvalue is destroyed at the end of the expression, if the variable had an associated thread of execution. - The single-argument
std::thread
constructor has been removed - This was UK issue 323. The variadic
std::thread
constructor provides all the necessary functionality.
There are also a few minor concurrency-related changes that have
been approved, mostly along the lines of clarifying the text. There
are a few more which are still under discussion, one of which is quite
significant: UK issue 329. This comment proposes the addition of a new
function std::async()
which will execute a function
asynchronously and return a std::unique_future
which can
be used to obtain the result. Details can be seen under LWG
issue 1043.
Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: C++0x, C++, standards, 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.
The Software Craftsmanship Manifesto
Wednesday, 11 March 2009
Do you care about the quality of your work as a software developer? Do you strive to produce the best software you can for your clients or employer? I don't mean basic level "does it work?" kind of quality — I hope we all aim to produce code that works. Does it matter to you if the code is well-crafted? Do you strive to write elegant software? Do you actively work to improve your skills as a developer?
There's been a lot of discussion about software quality on the internet recently. Uncle Bob, Joel Spolsky and Jeff Atwood got involved in the "Quality doesn't matter" debate, culminating in Uncle Bob talking on Jeff and Joel's Stack Overflow Podcast. James Bach even went as far as to hypothesise that Quality is Dead.
James has a point: in many instances it seems that people are quite happy to tolerate buggy software that's "good enough", and that developers are quite happy to ship such software. We're not perfect, and we will write code with bugs in, but to a large extent it's the attitude that counts. Whilst I accept that there may well be bugs in my code, I strive to avoid them, work hard to fix any that are found, and try and learn ways of reducing their occurrence in future. I also feel that software should be well-crafted so that it doesn't just work now, but will continue to work as it evolves, and such evolution should be as easy as possible. Of course, there's more to software quality than that — quality is Value to Some Person, and your job as a software developer is to ensure that your clients, customers or employers get the things that they value from the software you develop.
If this is something you feel strongly about, rest assured that you're not alone — there are many others who feel that Quality is Alive, to the extent that a few developers have got together to draft a Manifesto for Software Craftsmanship. The manifesto has over 1500 signatures (including mine) — why not add yours?
Posted by Anthony Williams
[/ design /] permanent link
Tags: software, craftsmanship, design, manifesto
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.
Multithreading in C++0x part 3: Starting Threads with Member Functions and Reference Arguments
Thursday, 26 February 2009
This is the third of a series of blog posts introducing the new C++0x thread library. The first two parts covered Starting Threads in C++0x with simple functions, and starting threads with function objects and additional arguments.
If you've read the previous parts of the series then you've seen how to start threads with functions and function objects, with and without additional arguments. However, the function objects and arguments are always copied into the thread's internal storage. What if you wish to run a member function other than the function call operator, or pass a reference to an existing object?
The C++0x library can handle both these cases: the use of member
functions with std::thread
requires an additional argument for the object on which to invoke the
member function, and references are handled with
std::ref
. Let's take a look at some examples.
Invoking a member function on a new thread
Starting a new thread which runs a member function of an existing
object: you just pass a pointer to the member function and a value to
use as the this
pointer for the object in to the std::thread
constructor.
#include <thread> #include <iostream> class SayHello { public: void greeting() const { std::cout<<"hello"<<std::endl; } }; int main() { SayHello x; std::thread t(&SayHello::greeting,&x); t.join(); }
You can of course pass additional arguments to the member function too:
#include <thread> #include <iostream> class SayHello { public: void greeting(std::string const& message) const { std::cout<<message<<std::endl; } }; int main() { SayHello x; std::thread t(&SayHello::greeting,&x,"goodbye"); t.join(); }
Now, the preceding examples both a plain pointer to a local object
for the this
argument; if you're going to do that, you
need to ensure that the object outlives the thread, otherwise there
will be trouble. An alternative is to use a heap-allocated object and
a reference-counted pointer such as
std::shared_ptr<SayHello>
to ensure that the object
stays around as long as the thread does:
#include <> int main() { std::shared_ptr<SayHello> p(new SayHello); std::thread t(&SayHello::greeting,p,"goodbye"); t.join(); }
So far, everything we've looked at has involved copying the
arguments and thread functions into the internal storage of a thread
even if those arguments are pointers, as in the this
pointers for the member functions. What if you want to pass in a
reference to an existing object, and a pointer just won't do?
That is the task of std::ref
.
Passing function objects and arguments to a thread by reference
Suppose you have an object that implements the function call
operator, and you wish to invoke it on a new thread. The thing is you
want to invoke the function call operator on this particular
object rather than copying it. You could use the member function
support to call operator()
explicitly, but that seems a
bit of a mess given that it is callable already. This is the
first instance in which std::ref
can help — if
x
is a callable object, then std::ref(x)
is
too, so we can pass std::ref(x)
as our function when we
start the thread, as below:
#include <thread> #include <iostream> #include <functional> // for std::ref class PrintThis { public: void operator()() const { std::cout<<"this="<<this<<std::endl; } }; int main() { PrintThis x; x(); std::thread t(std::ref(x)); t.join(); std::thread t2(x); t2.join(); }
In this case, the function call operator just prints the address of
the object. The exact form and values of the output will vary, but the
principle is the same: this little program should output three
lines. The first two should be the same, whilst the third is
different, as it invokes the function call operator on a copy
of x
. For one run on my system it printed the following:
this=0x7fffb08bf7ef this=0x7fffb08bf7ef this=0x42674098
Of course, std::ref
can be used for other arguments
too — the following code will print "x=43":
#include <thread> #include <iostream> #include <functional> void increment(int& i) { ++i; } int main() { int x=42; std::thread t(increment,std::ref(x)); t.join(); std::cout<<"x="<<x<<std::endl; }
When passing in references like this (or pointers for that matter),
you need to be careful not only that the referenced object outlives
the thread, but also that appropriate synchronization is used. In this
case it is fine, because we only access x
before we start
the thread and after it is done, but concurrent access would need
protection with a mutex.
Next time
That wraps up all the variations on starting threads; next time we'll look at using mutexes to protect data from concurrent modification.
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.
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.
Multithreading in C++0x part 1: Starting Threads
Tuesday, 10 February 2009
This is the first of a series of blog posts introducing the new C++0x thread library.
Concurrency and multithreading is all about running multiple pieces of code in parallel. If you have the hardware for it in the form of a nice shiny multi-core CPU or a multi-processor system then this code can run truly in parallel, otherwise it is interleaved by the operating system — a bit of one task, then a bit of another. This is all very well, but somehow you have to specify what code to run on all these threads.
High level constructs such as the parallel algorithms in Intel's Threading Building
Blocks manage the division of code between threads for you, but we
don't have any of these in C++0x. Instead, we have to manage the
threads ourselves. The tool for this is std::thread
.
Running a simple function on another thread
Let's start by running a simple function on another thread, which
we do by constructing a new std::thread
object, and passing in the function to the constructor. std::thread
lives in the <thread>
header, so we'd better
include that first.
#include <thread> void my_thread_func() {} int main() { std::thread t(my_thread_func); }
If you compile and run this little app, it won't do a lot: though it starts a new thread, the thread function is empty. Let's make it do something, such as print "hello":
#include <thread> #include <iostream> void my_thread_func() { std::cout<<"hello"<<std::endl; } int main() { std::thread t(my_thread_func); }
If you compile and run this little application, what happens? Does
it print hello
like we wanted? Well, actually there's no
telling. It might do or it might not. I ran this simple application
several times on my machine, and the output was unreliable: sometimes
it output "hello", with a newline; sometimes it output "hello"
without a newline, and sometimes it didn't output
anything. What's up with that? Surely a simple app like this ought to
behave predictably?
Waiting for threads to finish
Well, actually, no, this app does not have
predictable behaviour. The problem is we're not waiting for our thread
to finish. When the execution reaches the end of main()
the program is terminated, whatever the other threads are doing. Since
thread scheduling is unpredictable, we cannot know how far the other
thread has got. It might have finished, it might have output the
"hello"
, but not processed the std::endl
yet, or it might not have even started. In any case it will be
abruptly stopped as the application exits.
If we want to reliably print our message, we have to
ensure that our thread has finished. We do that by joining
with the thread by calling the join()
member function of our thread object:
#include <thread> #include <iostream> void my_thread_func() { std::cout<<"hello"<<std::endl; } int main() { std::thread t(my_thread_func); t.join(); }
Now, main()
will wait for the thread to finish before
exiting, and the code will output "hello" followed by a newline every
time. This highlights a general point: if you want a thread to
have finished by a certain point in your code you have to wait for
it. As well as ensuring that threads have finished by the
time the program exits, this is also important if a thread has access
to local variables: we want the thread to have finished before the
local variables go out of scope.
Next Time
In this article we've looked at running simple functions on a separate thread, and waiting for the thread to finish. However, when you start a thread you aren't just limited to simple functions with no arguments: in the second part of this series we will look at how to start a thread with function objects, and how to pass arguments to the thread.
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.
Designing Multithreaded Applications with C++0x: ACCU 2009
Tuesday, 13 January 2009
The schedule for ACCU 2009 in Oxford was announced earlier today, and I am pleased to say that I will be speaking on "Designing Multithreaded Applications with C++0x" on Thursday 23rd April 2009.
As has become customary, the main conference will run from Wednesday to Saturday, with a day of pre-conference workshops on Tuesday 21st April 2009. There is a whole host of well-known speakers, including "Uncle Bob" Martin, Linda Rising, Michael Feathers and Andrei Alexandrescu, so the conference should be excellent value, as ever.
If you book before the end of February, you can take advantage of the "Early Bird" rates.
I hope to see you there!
Posted by Anthony Williams
[/ news /] permanent link
Tags: concurrency, threading, accu, C++0x
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.
just::thread C++0x Thread Library V1.0 Released
Thursday, 08 January 2009
I am pleased to announce that version 1.0 of just::thread, our C++0x Thread Library is now available.
The just::thread
library is a complete implementation
of the new C++0x thread library as per the current
C++0x working paper. Features include:
std::thread
for launching threads.- Mutexes and condition variables.
std::promise
,std::packaged_task
,std::unique_future
andstd::shared_future
for transferring data between threads.- Support for the new
std::chrono
time interface for sleeping and timeouts on locks and waits. - Atomic operations with
std::atomic
. - Support for
std::exception_ptr
for transferring exceptions between threads. - Special deadlock-detection mode for tracking down the call-stack leading to deadlocks, the bane of multithreaded programming.
The library works with Microsoft Visual Studio 2008 or Microsoft
Visual C++ 2008 Express for 32-bit Windows. Don't wait for a full
C++0x compiler: Buy your copy of
just::thread
now and start using the C++0x thread
library in minutes.
Posted by Anthony Williams
[/ news /] permanent link
Tags: multithreading, concurrency, C++0x
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.
The Most Popular Articles of 2008
Monday, 05 January 2009
Five days into 2009, here's a list of the 10 most popular articles on the Just Software Solutions website for 2008. There's a few entries still there from last year (in particular, last year's most top entry on CSS menus is now number 2), but mostly it's new content. In 2008 I focused much more on C++0x and concurrency, and the list of popular articles reflects that.
- Implementing
a Thread-Safe Queue using Condition Variables
A description of the issues around writing a thread-safe queue, with code. - Implementing
drop-down menus in pure CSS (no JavaScript)
How to implement drop-down menus in CSS in a cross-browser fashion (with a teensy bit of JavaScript for IE). - 10
Years of Programming with POSIX Threads
A review of "Programming with POSIX Threads" by David Butenhof, 10 years after publication. - Thread
Interruption in the Boost Thread Library
A description of the thread interruption feature of the Boost Thread library. - Introduction
to C++ Templates (PDF)
How to use and write C++ templates. - Memory
Models and Synchronization
A brief description of the relaxed memory orderings of the C++0x memory model - Deadlock
Detection with just::thread
How to use thejust::thread
C++0x thread library to detect the origin of deadlocks in your code. - Rvalue
References and Perfect Forwarding in C++0x
An introduction to the new rvalue reference feature of C++0x. - October
2008 C++ Standards Committee Mailing - New C++0x Working Paper, More
Concurrency Papers Approved
My summary of the October 2008 C++ committee mailing featuring the first feature-complete draft of the C++0x standard. - Condition
Variable Spurious Wakes
An introduction to the consequences of the so-called "spurious wakes" that you can get with condition variables, and how to handle them.
Posted by Anthony Williams
[/ news /] permanent link
Tags: popular, articles
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