[C++11 并发编程] 07 - Mutex 死锁
来源:互联网 发布:python 金融数据接口 编辑:程序博客网 时间:2024/05/21 22:23
假设有两个线程,在执行某些操作时,都需要锁定一对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_localvoid 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 并发编程] 07 - Mutex 死锁
- 【并发编程】死锁
- [C++11 并发编程] 05 - Mutex 基本操作
- [C++11 并发编程] 06 - Mutex race condition
- [C++11 并发编程] 08 - Mutex std::unique_lock
- c++11并发编程指南三(std::mutex 详解)
- Java并发编程实战--死锁
- Java 并发编程之死锁
- 并发编程之避免死锁
- Java并发编程:线程死锁
- Linux编程学习之线程篇-Mutex的死锁
- mutex死锁追踪
- 【C/C++开发】C++11 并发指南三(std::mutex 详解)
- C++11并发之std::mutex
- 并发编程二:HashMap怎么会死锁呢?
- 并发编程 - 死锁,活锁和饥饿
- Java并发编程:死锁及解决方法
- java并发编程(十一)--死锁
- hdu 1870 愚人节的礼物 Java 水题系列
- 计算机视觉方面2016年重要会议deadline
- Linux 命令
- Xcode中iOS模拟器程序中的plist路径
- HDU 1151 Air Raid( 最小路径覆盖)
- [C++11 并发编程] 07 - Mutex 死锁
- 栈和堆的区别
- 自用图形解锁分析
- Hello world!Blog.
- 九度oj 1036
- HDOJ-1787 GCD Again(欧拉函数)
- python中IndentationError
- 栈
- Android Map根据键或者值进行排序