Data protection
Like many thread APIs, c++0x protects shared data with mutual exclusion. There are four types of mutexes:
Non-recursive (Std::mutex)
Recursive (Std::recursive_mutex)
Allow lock timeout non-recursive (Std::timed_mutex)
Allow lock timeout recursive (Std::recursive_timed_mutex)
If you try to Cheng (lock) a non-recursive mutex two times without unlock it, an unknown result will occur. Recursive recur6sive mutexes only increase the count of locks, so you must ensure that you unlock and lock the same number of times that other threads may lock the mutex.
Usually we use template class std::unique_lock<> and std::lock_guard<> to lock and unlock a mutex. These classes lock a mutex in the constructor and unlock it in the destructor. Therefore, if you are using a local variable, your mutex will automatically be unlock when exiting the scope.
std::mutex m;
my_class data;
void foo()
{
std::lock_guard<std::mutex> lk(m);
process(data);
} // mutex unlocked here
Std::lock_guard can only be used like this. Std::unique_lock allows you to delay lock, set timeouts, and unlock before the object is destroyed. If you choose Std::timed_mutex to set the lock timeout, you need to use Std::unique_lock:
std::timed_mutex m;
my_class data;
void foo()
{
std::unique_lock<std::timed_mutex>
lk(m,std::chrono::milliseconds(3)); // wait up to 3ms
if(lk) // if we got the lock, access the data
process(data);
} // mutex unlocked here
Because these lock classes are templates, they can be used with all standard mutex types, as well as extension types that provide lock () and unlock () functions.
Avoid deadlocks
Sometimes, we need to lock multiple mutexes. Failure to control can lead to deadlock (deadlock): Two threads attempt to lock the same mutex, each thread locks a mutex, and waits for another thread to release the other mutex. c++0x Considering this problem, you can use the Std::lock function to lock multiple mutexes one at a time without risking the risk of deadlocks:
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);
std::lock(lock_a,lock_b);
// do something with the internals of a and b
}
In the above example, if you do not use Std::lock, it will likely lead to deadlocks (such as one thread executing foo (x,y) and the other executing foo (y,x)). After adding std::lock, it is safe.