RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Simpler Multithreading in C++0x : Page 2

The new standard will support multithreading, with a new thread library. Find out how this will improve porting code, and reduce the number of APIs and syntaxes you use.

Protecting Data
In the C++0x thread library, as with most thread APIs, the basic facility for protecting shared data is the mutex. In C++0x, there are four varieties of mutexes:
  • non-recursive (std::mutex)
  • recursive (std::recursive_mutex)
  • non-recursive that allows timeouts on the lock functions (std::timed_mutex)
  • recursive mutex that allows timeouts on the lock functions (std::recursive_timed_mutex)
All of them provide exclusive ownership for one thread. If you try and lock a non-recursive mutex twice from the same thread without unlocking in between, you get undefined behavior. A recursive mutex simply increases the lock count—you must unlock the mutex the same number of times that you locked it—in order for other threads to be allowed to lock the mutex.

Though these mutex types all have member functions for locking and unlocking, in most scenarios the best way to do it is with the lock class templates std::unique_lock<> and std::lock_guard<>. These classes lock the mutex in the constructor and release it in the destructor. Thus, if you use them as local variables, your mutex is automatically unlocked when you exit the scope:

    std::mutex m;
    my_class data;

    void foo()
        std::lock_guard<std::mutex> lk(m);
    }   // mutex unlocked here
std::lock_guard is deliberately basic and can only be used as shown. On the other hand, std::unique_lock allows for deferred locking, trying to lock, trying to lock with a timeout, and unlocking before the object is destroyed. If you've chosen to use std::timed_mutex because you want the timeout on the locks, you probably need to use std::unique_lock:

    std::timed_mutex m;
    my_class data;

    void foo()
            lk(m,std::chrono::milliseconds(3)); // wait up to 3ms
        if(lk) // if we got the lock, access the data
    }   // mutex unlocked here
These lock classes are templates, so they can be used with all the standard mutex types, plus any additional types that supply lock() and unlock() functions.

Protecting Against Deadlock When Locking Multiple Mutexes
Occasionally, an operation requires you to lock more than one mutex. Done wrong, this is a nasty source of deadlocks: Two threads can try and lock the same mutexes in the opposite order, with each end upholding one mutex and waiting for the other thread to finish with the other mutexes. The C++0x thread library allievates this problem, in those cases where you wish to acquire the locks together, by providing a generic std::lock function that can lock multiple mutexes at once. Rather than calling the lock() member function on each mutex in turn, you pass them to std::lock(), which locks them all without risking deadlock. You can even pass in currently unlocked instances of std::unique_lock<>:

    struct X
        std::mutex m;
        int a;
        std::string b;

    void foo(X& a,X& b)
        std::unique_lock<std::mutex> lock_a(a.m,std::defer_lock);
        std::unique_lock<std::mutex> lock_b(b.m,std::defer_lock);

        // do something with the internals of a and b
In the above example, suppose you didn't use std::lock. This could possibly result in a deadlock if one thread did foo(x,y) and another did foo(y,x) for two X objects x and y. With std::lock, this is safe.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date