码迷,mamicode.com
首页 > 编程语言 > 详细

[C++11 并发编程] 06 - Mutex 死锁

时间:2015-08-15 16:34:40      阅读:202      评论:0      收藏:0      [点我收藏+]

标签: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 并发编程] 06 - Mutex 死锁

标签:c++11   并发   mutex   死锁   hierarchical mutex   

原文地址:http://blog.csdn.net/yamingwu/article/details/47682261

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!