标签:c++11 并发 mutex 死锁 hierarchical mutex
假设有两个线程,在执行某些操作时,都需要锁定一对mutex,线程A锁定了mutex A,而线程B锁定了额mutex B,它们都在等待对方释放另一个mutex,这就会导致这两个线程都无法继续执行。这种情况就是死锁。
避免死锁最简单的方法是总是以相同的顺序对两个mutex进行锁定,比如总是在锁定mutex B之前锁定mutex A,就永远都不会死锁。
假设有一个操作要交换同一个类的两个实例的内容,为了交换操作不被并发修改影响,我们需要锁定这两个实例内部的mutex。但是,如果选定一个固定的顺序来锁定mutex(线锁定第一个参数指定对象的mutex,再锁定第二个参数指定对象的mutex)恰恰适得其反,会导致死锁。
针对这种情况,我们需要使用C++11标准库的std::lock操作来解决这个问题。lock函数可以接受两个或者多个mutex以避免死锁。
示例如下:
#include <mutex>
class some_big_object
{};
void swap(some_big_object& lhs,some_big_object& rhs)
{}
class X
{
private:
some_big_object some_detail;
mutable std::mutex m;
public:
X(some_big_object const& sd):some_detail(sd){}
friend void swap(X& lhs, X& rhs)
{
// 判断两个实例是否相同,如果相同则直接退出
// 因为锁定已经被本线程锁定的mutex的结果是不确定的
if(&lhs==&rhs)
return;
// 锁定两个mutex
std::lock(lhs.m,rhs.m);
// 创建两个lock_guard实例,分别给它们传入一个mutex对象
// std::adopt_lock参数表明所传入的mutex已经被锁定了,它们只需要接受已锁定mutex的所有权
// 而不需要在它的构造函数中尝试锁定这个传入的mutex
std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
swap(lhs.some_detail,rhs.some_detail);
}
};
int main()
{}使用std::lock_guard确保在程序抛出异常对出时,也能正确的对锁定的mutex进行解锁。
std::lock操作可以保证在成功锁定第一个mutex后,如果在尝试锁定第二个mutex时发生异常,第一个mutex会被自动解锁。std::lock操作只能在锁定多个mutex时帮助我们避免死锁,如果这些mutex是一个个被单独锁定的,就需要我们自己在实现的时候保证程序不会发生死锁。接下来我们看看,避免死锁的几个基本方法:
1. 避免嵌套锁:
如果每个线程都只能锁定一个mutex,则不会发生死锁。如果要使用多个锁,就使用std::lock。
2. 避免在锁定了一个mutex后调用用户提供的代码:
我们无法保证用户代码做了什么,其很有可能锁定其它的mutex而导致死锁。
3. 以固定的顺序锁定mutex:
如果确实需要锁定多个mutex,而这些mutex可以被同时锁定,使用前面我们讲到std::lock方法来保证锁定mutex的顺序而避免死锁。如果这些mutex只能分别被锁定,则需要在实现时保证锁定mutex的顺序是固定的,比如总是在锁定B之前锁定A,在锁定C之前锁定B。
4. 使用分层锁:
把程序分成几个层次。区分每个层次中使用的锁,当一个线程已经持有更低层次的锁时,不允许使用高层次的锁。可以在程序运行时给不同的锁加上层次号,记录每个线程持有的锁。
#include <stdexcept>
#include<thread>
#include<mutex>
class hierarchical_mutex//实现mutex的三个接口lock,unlock,trylock
{
std::mutex internal_mutex;
unsigned long const hierarchy_value;//记录mutex所在层次
unsigned long previous_hierarchy_value;//记录上一个mutex的层次,解锁时恢复线程的层次
//thread_local每一个线程都有自己的该全局变量的实例(instance)
static thread_local unsigned long this_thread_hierarchy_value;//线程所在层次,是thread_local
void check_for_hierarchy_violation()
{ //线程所在层次要大于当前的mutex的层次,否则抛出异常
if (this_thread_hierarchy_value <= hierarchy_value)
{
throw std::logic_error("mutex hierarchy violated");
}
}
void update_hierarchy_value()
{
previous_hierarchy_value = this_thread_hierarchy_value;
this_thread_hierarchy_value = hierarchy_value;
}
public:
explicit hierarchical_mutex(unsigned long value) :
hierarchical_mutex(value), previous_hierarchy_value(0){}
void lock()
{
//先检查、再上锁、再更新层次
check_for_hierarchy_violation();
internal_mutex.lock();
update_hierarchy_value();
}
void unlock()
{
//更新层次、再解锁
this_thread_hierarchy_value = previous_hierarchy_value;
internal_mutex.unlock();
}
bool try_lock()
{
check_for_hierarchy_violation();
if (!internal_mutex.try_lock())
return false;
update_hierarchy_value();
return true;
}
};
thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value(ULLONG_MAX);版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:c++11 并发 mutex 死锁 hierarchical mutex
原文地址:http://blog.csdn.net/yamingwu/article/details/47682261