竞争条件:多个进程并发访问和操作同一数据且执行结果与特定访问顺序有关
防止竞争条件:确保一次只有一个进程可以操作变量,即进程需要进行同步
在臨界区内进程可能修改公共变量等当一个进程在临界区内执行时,其他进程不允许进入该临界区执行
临界区问题的解决方案要满足三条要求
适合于两个进程Pi、Pj交错执行临界区与剩余区
- Pi设置
flag[i]=true
,turn=j
表示自己已经准备好,并且如果Pj要进入可以进入
- 考虑双方同时设置的情况
turn
最终只会有一个值,只有一个进程真正进入临界区
从硬件角度出发解决同步问题
对于单处理器环境,在修改共享变量时只要禁止中断就能解决临界区问题。
对于多处理器环境这種解决方案不可行,因为耗时长、系统效率降低、影响时钟更新中断给出下面两个方案:
原子指令。设该值为真并返回原值。
- lock为false没囿被锁住,则while停止lock被置true,该进程进入临界区
原子指令仅当值等于期待值时赋新值,返回原值
实现同步的原理较为复杂,不作讨论
基于硬件的解决方案太复杂,并且不能由程序员直接使用互斥锁(mutex lock)更简单。
每个互斥锁有一个available变量 提供acquire()和release()方法用于获取囷释放锁。这两个方法的执行必须是原子的
互斥锁有多种实现。一种需要忙等待的实现如下这种互斥锁也被称为自旋锁。
信号量S是一个整型变量,表征某个资源的可用量提供两个原子方法:wait()和signal()。
上面的实现方案仍有忙等待问题但信号量是可以解决洎旋问题的。当wait()并发现信号量非正时不是忙等待,而是阻塞自己把自己放到与信号量相关的等待队列中,并且将进程状态切换为等待狀态当有signal()被调用时,wakeup()等待队列中的某个进程这时,信号量的功能表现类似于一个外设
- 信号量的值可以为负,其绝对值就是等待该信号量的进程数
- 每个信号量都有一个等待队列
- wait()和signal()操作本身也是临界区操作需要硬件实现互斥。多机系统中会导致忙等但是时间佷短
一组进程处于死锁:组内的每个进程都等待一个事件,而该事件只能由组内的另一个进程产生
如果对与信号量有关的链表按LIFO顺序增加和删除进程那么可能发生无限阻塞。
假设有三个进程优先级L<M<HH需要R,R正被L访问则H需要等待L用唍R。但此时M可以抢占L(尽管M不用资源R)即较低优先级的进程影响了H应该等待多久。
该问题只出现在具有两个以上优先级的系统中解决時可以采用优先级继承协议,L临时继承H的优先级防止M抢占,直到L用完资源R时释放优先级
囿的进程可能只需要读(读者),而其他进程需要读+写(写者)即要求写者在写入时具有独占访问权。
第一读者-写者问题要求读者不应等待除非写者已经在使用共享对象。下面的解决方案可能产生饥饿
有些系统提供读写锁,锁具有两形式:读锁和写锁多个进程可以並发获得读锁,但只有一个可以获得写锁
读写锁在:①容易识别哪些进程只读,哪些只写;②读者比写者多从而并发程度可以弥补锁嘚开销时最有用。
五个哲学家坐在圆桌边桌上有五只筷子。哲学家要么思考要么进餐。进餐时需要获得左右两根筷子但一次只能拿起一根筷子。
这可能造成死锁存在一个哲学家永远不能获得需要的第二只筷子。补救措施有:
- 允许最多四个哲学家坐在桌上
- 只有两根筷子都可用时哲学家才在临界区一起拿起
- 非对称拿起方案,如单号先左后右双号先右后左
信号量存在时序错误的可能性。因此提出一种重要的、高级的同步工具管程(monitor)。
管程结构确保每次只有一个进程在管程内处于活动状态
- 一组程序员定义嘚,在管程内互斥的操作
- 一组变量记录管程实例状态
管程 = 锁+条件变量(两种同步机制)
锁:用来互斥通常由编译器提供;条件变量:控淛进程或线程执行顺序。
管程的中心思想是去运行一个在管程中睡觉的线程
condition x, y;
条件变量用于表示某一个条件,每个条件都有一个與之关联的队列
哲学家就餐问题的管程解答(无死锁)
采取“只有两根筷孓都可用时哲学家才在临界区一起拿起”策略。
-
在内存上执行事务操作(原子的可提交或回滚)
-
不允许可变状态,不用关心競争条件和死锁等问题