操作系统导论习题解答(32. Concurrency Bugs)

Common Concurrency Problems

带着问题:如何处理常见的并发错误问题?

1. What Types Of Bugs Exist?

主要研究4个开源应用:

  1. MYSQL
  2. Apache
  3. Mozilla
  4. OpenOffice

在这里插入图片描述

2. Non-Deadlock Bugs

大部分并发问题都是non-deadlock bugs。此类问题主要分为两类:

  1. atomicity violation bugs
  2. order violation bugs

2.1 Atomicity-Violation Bugs

下图是MYSQL的一个并发问题:

在这里插入图片描述
上述代码会产生问题:线程1检查了thd->proc_info为非NULL值,准备进入fputs时被打断;然后线程2开始运行,把本来不是NULL值的thd->proc_info变成了NULL值;最后线程1再继续运行时发生崩溃。

解决这类问题可以使用锁(lock)
在这里插入图片描述

2.2 Order-Violation Bugs

下图是另一方面并发问题:
在这里插入图片描述
上述代码问题:线程2运行时它觉得mThread已经存在了,如果mThread为NULL或未初始化,那么线程2就会崩溃。

解决这类问题可以使用条件变量(condition variables)和锁(lock)
在这里插入图片描述

2.3 Non-Deadlock Bugs: Summary

不是所有的non-deadlock bugs都能很好的如上述方法处理,一部分Bugs需要我们对其有很深的理解。

3. Deadlock Bugs

一个双方都等待对方释放锁的例子:
在这里插入图片描述
上述代码不是一定发生死锁(deadlock),而只是有可能:线程1获取锁L1后发生线程转换,线程2获取锁L2,这样才会产生deadlock

在这里插入图片描述

3.1 Why Do Deadlocks Occur?

产生死锁的原因:

  1. 在大型代码库中,组件之间会产生复杂的依赖关系(complex dependencies)。
  2. 封装(encapsulation)的性质。

3.2 Conditions for Deadlock

发生死锁需要满足四个条件:

  1. Mutual exclusion
  2. Hold-and-wait
  3. No preemption
  4. Circular wait

上述任一条件不满足都不会发生死锁,所以解决死锁可以从4四个方面入手。

3.3 Prevention

3.3.1 Circular Wait

解决circular wait最直接的方法就是在获取锁的时候提供total ordering(总顺序)。当然,对于小型系统而言,能够简单实现;但是对于大型系统,锁非常多,提供总顺序不太可能,所以,一个有效的方法就是提供部分顺序(partial ordering)

3.3.2 Hold-and wait

解决该问题的方法如下:一次获取所有锁
在这里插入图片描述

3.3.3 No Preemption

很多系统都提供很灵活的接口处理这个问题。一个例子如下:
在这里插入图片描述
上述方法解决死锁但是又产生了一个新的问题活锁(livelock):两个线程都可以反复尝试此代码,而又多次未能获取两个锁。

解决livelock的方法就是使用trylock:线程获取锁L1后,获取锁L2之前必须释放获取锁L1时分配的内存。

3.3.4 Mutual Exclusion

解决此类问题方法:使用功能强大的硬件指令,构造不需要显式锁的数据结构

看一个例子,假设要调用硬件提供的指令compare-and-swap:
在这里插入图片描述
要做加值操作:
在这里插入图片描述
可以看到关键部分没有加锁,而是使用硬件提供的指令

再看一个链表插入例子:
在这里插入图片描述
上述代码执行简单插入,但是在多线程中会造成race conditon

对其进行修改:
在这里插入图片描述
上述代码对关键部分加锁。我们接下来不使用锁而使用硬件指令:
在这里插入图片描述
上述代码在多线程中有个问题:如果其他线程同时成功换入了新的头节点,则此操作失败,从而导致该线程使用新的头节点再次重试

4. Deadlock Avoidance via Scheduling

在某些场景下避免死锁发生比阻止死锁发生更好。
假设有两个锁,四个线程。每个线程获取锁的状态如下:
在这里插入图片描述
只要T1、T2不同时运行,就不会发生死锁。调度程序可以如下:
在这里插入图片描述
注:T3和T1、T3和T2可以交替执行

再看另一种情况:
在这里插入图片描述
由于T1、T2、T3都获取锁L1、L2,故只要T1、T2、T3不同时运行,就不会发生死锁。
在这里插入图片描述

5. Detect and Recover

最后一种解决死锁的策略:允许死锁发生,但是发生后对其进行相应的处理

最简单的例子:操作系统运行了一个月,你只需重启就行了。

posted @ 2022-10-14 19:10  astralcon  阅读(45)  评论(0编辑  收藏  举报