【C++ 并发与多线程】std::thread类-为共享数据加锁 2

正交——消除无关事务之间的影响,力求高内聚低耦合。

 死锁的概念略去不说,死锁有可能发生在使用多个互斥量的场景下,也可能存在没有使用互斥量的场景:

  • 两个线程都在等待对方释放互斥量
  • 两个线程都调用了对方的join()函数

为了解决两个线程都在等待对方释放互斥量导致的死锁问题,C++11提供了若干机制:

  • std::lock()函数
  • std::unique_lock类

    锁住所有互斥量

    只要将互斥量作为参数传递给std::lock(),std::lock()就能够锁住多个互斥量。std::lock()并未指定解锁和上锁的顺序,其能够保证:

  • std::lock()执行成功时,所有互斥量都已经被上锁,并且没有死锁问题
  • std::lock()执行失败时,已被其上锁的互斥量都会被解锁
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::lock

class some_big_object
{
public:
	some_big_object(int a) :x(a) {}
	void Print() { std::cout << x << std::endl; }
private:
	int x;
};
class X
{
private:
	some_big_object& some_detail;
	std::mutex m;
public:
	X(some_big_object & sd) :some_detail(sd) {}
	friend void swap(X& lhs, X& rhs)
	{
		if (&lhs == &rhs)
			return;
		std::lock(lhs.m, rhs.m);
		std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
		std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
		std::swap(lhs.some_detail, rhs.some_detail);
	}
};

template<class T>
void swap(T& lhs, T& rhs);

template<>
void swap<some_big_object>(some_big_object &x, some_big_object &y)
{
	X a(x), b(y);
	swap(a, b);
}

int main()
{
	some_big_object a(1), b(2);
	a.Print(), b.Print();
	swap(a, b);
	a.Print(), b.Print();
	return 0;
}
------------------------------------------------------------
1
2
2
1
请按任意键继续. . .

上面一段代码使用了模板的偏特化特性,这里不需要深究,只需要知道swap(a, b)最终会调用X类的swap友元函数。在该友元函数中,std::lock()函数锁住两个互斥量,std::lock_guard负责unlock两个互斥量,如果不调用std::lock_guard(),需要手动unlock()。std::adopt_lock参数表示互斥量已经上锁,这里仅仅是不会重复上锁。下面两个例子起到相同作用。

// example 1
std::mutex mtx;
std::lock(mtx); // have to lock before the next sentence
std::lock_guard<std::mutex> guard(mtx, std::adopt_lock);

// example 2
std::mutex mtx;
std::lock(mtx);
mtx.unlock();

避免死锁的一点建议

C++并发编程中给出了几点避免死锁的进阶指导:

  • 1、避免嵌套锁
  • 2、避免在持有锁时调用用户提供的代码
  • 3、使用固定顺序获取锁
  • 4、使用锁的层次结构

前三个建议看字面意思就可以了,我们这里主要阐述锁的层次结构。层次锁需要遵守如下原则:

当代码试图对一个互斥量上锁,在该层锁已被低层持有时,上锁是不允许的。

hierarchical_mutex high_level_mutex(10000);
hierarchical_mutex low_level_mutex(7000); 
hierarchical_mutex low_level_mutex(5000); 

int do_low_level_stuff();
int low_level_func()
{
  std::lock_guard<hierarchical_mutex> lk(low_level_mutex); 
  return do_low_level_stuff();
}

void high_level_stuff(int some_param);
void high_level_func()
{
  std::lock_guard<hierarchical_mutex> lk(high_level_mutex); 
  high_level_stuff(low_level_func()); 
}

void middle_level_stuff(int some_param);
void middle_level_func()
{
  std::lock_guard<hierarchical_mutex> lk(middle_level_mutex); 
  middle_level_stuff(high_level_stuff()); 
}

int main()
{
    high_level_func();
    middle_level_func();
}

按照层次锁的原则,high_level_func()能够正确执行,而middle_level_func()不能正确执行:

  • high_level_func()先获取到高层级的锁,然后获取到低层级的锁,符合原则
  • middle_level_func()先获取低层级的锁,然后获取到高层级的锁,不符合原则
class hierarchical_mutex
{
  std::mutex internal_mutex;
  unsigned long const hierarchy_value;
  unsigned long previous_hierarchy_value;
  static thread_local unsigned long this_thread_hierarchy_value; 
  void check_for_hierarchy_violation()
  {
    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):
      hierarchy_value(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(ULONG_MAX);

引用来源: C++并发编程2——为共享数据加锁(三)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值