避免死锁的一些注意事项

1. 避免嵌套锁, 如果每个线程都只占有一个锁, 则可以很大程度上避免死锁。
其死锁的情况是, 线程 1 依次获得 A 对象和 B 对象的锁, 然后决定等另一个线程的信号再继续, 从而先释放了 B 对象的的锁。
可是线程 2 需要同时拥有对象 A 和对象 B 的锁才能向线程 1 发信号。
从而导致, 线程 2 因无法获得对象 A 上的锁, 因而阻塞, 等待线程 1 释放对象 A 上的锁。 而同时线程 1 也一直阻塞, 等待线程 2 的信号, 因此不会释放对象 A 的锁。

2. 用固定的顺序获取锁
如果非要同时拥有多个锁, 同时无法拥有单个的锁, 那么最好的处理方式是以固定的顺序得到他们。典型的情况是在一个链表的结构中, 如果要取得节点 B 的锁, 那么需要先取得节点 A 的锁, 接着取得节点 C 的锁。

3. 设置优先级
当多个锁存在时, 设立优先级是个好主意,这样可以避免低优先级的 mutex 会先于高优先级的拥有锁。例如以下代码:

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

int do_low_level_stuff();

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

void high_level_stuff(int some_param);

void high_level_func()
{
    lock_guard<hierarchical_mutex> lk(high_level_mutex);
    high_level_stuff(low_level_func);
}

void thread_a()
{
    high_level_func();
}

hierarchical_mutex other_mutex(100);
void do_other_stuff();

void other_stuff()
{
    high_levek_func();
    do_other_stuff();
}

void thread_b()
{
    lock_guard<hierarchical_mutex> lk(other_mutex);
    other_stuff();
}

其中, thread_a 就会成功, 而 therad_b 就会运行出错。
为啥呢?
因为它首先会拥有 other_mutex 的锁,other_mutex 的优先级只有 100, 却在  high_level_mutex 前得到锁, 这违反了优先级, 所以 hierarchical_mutex 会报错并抛出一个异常, 然后退出程序。
上文中提到的 hierarchical_mutex 并不是标准库的一部分, 但是可以很容易的实现:

class hierarchical_mutex
{
    mutex                                   internal_mutex;
    unsigned long const                     hierarchy_value;
    unsigned                                previous_hierarchy_value;
    static  thread_local unsigned long    this_thread_hierarchy_value;

    // second mutex must now be less than
    //that of the mutex already held   //in order for the check to pass
    void check_for_hierarcy_violation()
    {
        if(this_thread_hierarchy_value <= hierarchy_value){
            throw std::logic_error("mutex hierarchical 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_hierarcy_violation();
            internal_mutex.lock();
            update_hierarchy_value();
        }

        void unlock()
        {
            this_thread_hierarchy_value = previous_hierarchy_value;
            internal_mutex.unlock();
        }

        bool try_lock()
        {
            check_for_hierarcy_violation();
            if(!internal_mutex.try_lock()){
                return false;
            }

            update_hierarchy_value();
            return true;
        }
};

// initialized to the maximum value,
// so initially any mutex can be locked
thread_local unsigned long
    hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);
时间: 2024-12-25 19:19:20

避免死锁的一些注意事项的相关文章

细聊MySQL的Innodb存储引擎(完)

细聊MySQL的Innodb存储引擎(一) 细聊MySQL的Innodb存储引擎(二) 细聊MySQL的Innodb存储引擎(完) 上篇主要和大家探讨了Innodb引擎中出现幻读的处理方法与死锁的探测及避免死锁的一些注意事项.此篇,我们来研究下Innodb的索引. Innodb里涉及到的索引主要有四种,分别为聚簇索引(Clustered Index).次级索引(Secondary Index).全文索引(FULLTEXT Index).哈希索引(Hash Index). 聚簇索引与次级索引 每一

30分钟全面解析-SQL事务+隔离级别+阻塞+死锁

以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [

编写高性能SQL的注意事项

在数据库部分,对数据库应用性能改进来说,需要重点关注应用程序,在查询设计和索引策略等方面进行优化,甚至可以把数据库查询效率提高数百倍,在其他方面的优化努力,其效果就没有这么明显(见下图).本文重点描述在应用程序中进行数据库查询时,在设计和使用索引.设计查询语句等方面的注意事项,以取得良好的数据库查询性能. 一.索引设计和使用策略 使用索引是数据库减少磁盘I/O最有效的方法.除了在表中数据量非常少和需要返回表中大部分行的情况,正确使用索引的查询性能比全表扫描要高得多,有时简单新建一个合适的索引,就

SQL Server 收集数据库死锁信息

背景 我们在数据库出现阻塞及时邮件预警提醒中监控了数据库的阻塞情况,为了更好的维护数据库,特别是提升终端客户用户体验,我们要尽量避免在数据库中出现死锁的情况.我们知道收集死锁可以开启跟踪标志如1204,然后在日志中查看死锁相关信息,或者使用Profiler去跟踪死锁,我们希望所有的死锁信息收集到某表供我们后期优化分析使用,我们可以使用相对比较轻量的自带扩展事件(system_health)来完成这个需求. 测试环境 Microsoft SQL Server 2012 - 11.0.2100.60

【故障处理】队列等待之TX - allocate ITL entry引起的死锁处理

[故障处理]队列等待之TX - allocate ITL entry引起的死锁处理 1  BLOG文档结构图       2  前言部分 2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① enq: TX - allocate ITL entry等待事件的解决 ② 一般等待事件的解决办法 ③ 队列等待的基本知识 ④ ITL死锁解决 ⑤ ITL死锁模拟 ⑥ Merge语句的非关联形式的查询优化   Tips: ①

DB2死锁的解决办法

db2 get snapshot for locks on sampledb2 get db cfg for sampledb2 update db cfg using dlchktime 10000 -查看数据库管理器级别快照信息     db2 get snapshot for dbm -查看数据库级别快照信息     db2 get snapshot for database on dbname        -查看应用级别快照信息     db2 get snapshot for app

MySQL 5.5 主从复制异步、半同步以及注意事项详解

大纲 一.前言 二.Mysql 基础知识 三.Mysql 复制(Replication) 四.Mysql 复制(Replication)类型 五.Mysql 主从复制基本步骤 六.Mysql 主从复制(异步) 七.Mysql 主从复制(半同步) 八.Mysql 复制工具 九.Mysql 复制注意事项 十.Mysql 复制过滤 一.前言 从这一篇博客开始我们就来学习mysql的高级课程,在前面的几篇博客我们讲解了mysql基础知识.mysql日志类型.mysql配置文件.mysql备份策略,这一篇

.NET中lock的使用方法及注意事项

lock就是把一段代码定义为临界区,所谓临界区就是同一时刻只能有一个线程来操作临界区的代码,当一个线程位于代码的临界区时,另一个线程不能进入临界区,如果试图进入临界区,则只能一直等待(即被阻止),直到已经进入临界区的线程访问完毕,并释放锁旗标. 其基本使用方式如下: C-sharp代码 class Test { //定义一个私有成员变量,用于Lock private static object lockobj = new object(); void DoSomething() { lock (

软考(软件设计师)注意事项(攻略)

  软件专业的朋友都会去参加软考,我在这里就叙述一下软考(特指软件设计师)的注意事项. l  关于复习时间的安排      1.      对于大学专业就是计算机科学与技术(软件工程方向)的学生,只要平常的专业课学科绩良好,复习就不用费太多功夫,本人真正的复习时间就是一个多月.大概算起来六个星期,比如14年下半年的软考时间是11月8号,那么专业扎实的同学十一过后就可以着手复习,对于平常的专业课成绩不是很理想的同学,可以把复习时间改成两个月,只要肯下劲,通过是不成问题.      2.