Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

What's New in Boost Threads?


Protecting Shared Data

The changes to Boost Threads aren't just in the thread management interface: The thread synchronization primitives have been changed, too. Probably the most obvious change is that the lock() and unlock() member functions are now public, and the scoped_lock types are just specializations of the new boost::unique_lock<> template. Because this is a template, it can be used with anything that implements the Lockable concept—which includes instantiations of boost::unique_lock<>! Though you wouldn't generally write boost::unique_lock <boost::unique_lock<some_mutex>>, it is helpful in those cases where you wish your code to work with any type of mutex, as users can pass in a lock object instead. For example, it is used as part of the implementation of the new lock() functions for locking more than one mutex together.

boost::unique_lock<> is also movable, so locks can be transferred between objects (and thus between scopes). This makes it much easier to ensure that the mutex is correctly unlocked even when the unlock() is in a different function to the lock().

The flexibility with unique_lock (and the old scoped_lock types) comes at a price—the implementation must hold a flag to indicate whether or not the lock object owns the lock on the mutex, and this must be checked in the destructor. For those cases where you only need strict scoped locking, the boost::lock_guard<> template provides a smaller, lighter option. It locks the mutex in the constructor and unlocks it in the destructor, cannot be moved and cannot be unlocked without destroying it.

A multiple-reader/single-writer mutex has also made a comeback in the form of boost::shared_mutex based on Howard Hinnant's proposal in N2094 (see www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2094.html). This has a brand new interface, which features the boost::shared_lock<> template for acquiring shared ownership. It also provides the necessary lock() and unlock() functions, so boost::unique_lock< boost::shared_mutex> can be used for acquiring exclusive ownership.

Locking Multiple Mutexes Without Deadlock

One of the biggest issues with writing multithreaded code is the possibility of deadlock. One case where it is particularly problematic is where you must acquire locks on more than one mutex in order to perform an operation. Unless this is done carefully, different threads could attempt to acquire the locks in different orders, and thus each end up holding some locks whilst waiting for the others: deadlock! If you can structure your code so that the locks can be acquired together, the 1.36.0 release of Boost provides a solution to that in the form of the boost::lock() and boost::try_lock() functions.

These function templates let you specify up to five individual Lockable objects to lock, or an iterator range. They will then attempt to lock all the objects. lock() will block until it can acquire all the locks, but will not hold any locks while it is blocking. The algorithm used ensures that lock() will not deadlock with any other thread that attempts to acquire the same set of locks, in whatever order. try_lock() will either acquire all or none of the set; the return value indicates the success or failure of the call, and which lock couldn't be acquired in the case of failure.

One case where they can be used is for the comparison of two objects that can also be modified by other threads, and so the member data must be protected by a mutex. If two threads try and compare the same two objects, they need to ensure their mutexes are locked in the same order to avoid deadlock. This is where boost::lock comes in: It enables both to be locked without the user having to worry about the order. In the following example, boost::lock is used to lock the mutexes, then ownership of those locks is adopted by some scoped_lock objects to ensure the locks are correctly released when the function exits:

struct my_class
{
    mutable boost::mutex m;
    int x,y;
};
bool operator<(my_class const& lhs,my_class const& rhs)
{
    boost::lock(lhs.m,rhs.m);
    boost::mutex::scoped_lock lk1(lhs.m,boost::adopt_lock);
    boost::mutex::scoped_lock lk2(rhs.m,boost::adopt_lock);
    return (lhs.x<rhs.x) ||
           ((lhs.x==rhs.x) && (lhs.y<rhs.y));
}

Enhanced Condition Variables

With Boost 1.35.0, the condition variable implementation was given a revamp to support interruption, and a new type of condition variable was added: boost::condition_variable_any. Whereas boost::condition_variable only works with boost::unique_lock<boost::mutex> (or its synonym, boost::mutex::scoped_lock), the wait functions for boost::condition_variable_any can be used with any mutex type. This means that you can now use boost::timed_mutex with boost::condition_variable_any, and boost::recursive_mutex (though care needs to be taken to ensure it isn't recursively locked, as only one level of unlocking will be done by wait()). Even boost::shared_lock<boost::shared_mutex> can be used, or a user-provided mutex type.

boost::condition_variable_any cv;
boost::shared_mutex sm;
bool update_ready();
void process_shared_data();
void wait_for_data()
{
    boost::shared_lock<boost::shared_mutex> lk(sm);
    cv.wait(lk,update_ready);
    process_shared_data();
}

Date-Time Integration

The final change since Boost 1.34 is the integration with Boost.DateTime. This has been a long time coming, and means that finally, you can specify timeouts as boost::posix_time::milliseconds(50) (for example) rather than doing a dance with boost::xtime. Absolute timeouts are still supported through the new boost::system_time typedef (and xtime, for backwards compatibility), but all thread functions that take a timeout now accept both absolute and relative timeouts.

Summary

The Boost Thread library has undergone some major changes in 2008. These changes help pave the way for the upcoming C++0x Standard, and make it easier for users to write robust multithreaded programs. This isn't the end of the line though: As the C++0x working draft evolves, the Boost Thread library will evolve with it, both to incorporate the changes and to provide a proving ground for new ideas.


Anthony is founder and managing director of Just Software Solutions. He is also maintainer of the Boost Thread library and author of the upcoming C++ Concurrency in Action: Practical Multithreading. He can be contacted at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.