一、背景
利用信号量和管程解决同步互斥的问题
1.并发问题:竞争条件(竞态条件)
- 多程序并发存在大的问题
2.同步
- 线程共享公共数据的协调条件
- 包括互斥与条件同步
- 互斥:在同一时间只有一个线程可以执行临界区
3.解决同步问题正确比较难
- 需要高层次的编程抽象(如:锁)
- 从底层硬件支持编译
二、信号量
1. 抽象数据类型
1)一个整形(sem),两个原子操作
2)p() 操作:sem减一
- 如果信号量sem<0,认为执行p操作的进程需要睡眠,等待
- 如果信号量sem>0,认为执行p操作的进程可以继续执行,可以进入临界区
- 如果挡住了,就不能执行后续的程序,起到了一个阻挡的作用。
3)v() 操作:sem加一
- 如果信号量sem<=0,认为当前的进程等待在这一个信号量上面,然后会唤醒这个进程(一个或多个)
2. 属性
-
信号量是整数(有符号数)
一开始通常会设定为一个大于0的数,所以一开始执行p操作不会被阻塞。但是多次执行p操作之后,执行p操
作的进程就会等待在上面。这时需要进程执行v操作,然后唤醒等在这个上面的进程。(如果只能唤醒一个进
程,一般是唤醒第一个等待的进程,FIFO队列)
-
信号量是被保护的变量
-
初始化完成后,唯一改变一个信号量的值的办法是通过p操作或者是v操作
-
操作必须是原子
-
-
p操作(信号量减一操作)能够阻塞,v操作(信号量加一操作)不会阻塞
-
假定信号量是公平的
- 没有线程被阻塞在p操作仍然阻塞如果v操作被无限频繁地调用(在同一个信号量)
- 在实践中,FIFO经常被使用
3. 两种类型信号量
- 二进制信号量:可以是0或1(与前面的lock达到同样的效果)
- 一般/计数信号量:可取任何非负值
- 两者互相表现(给定一个可以实现另一个)
4. 注意
- 信号量可以用在两个方面
- 互斥
- 条件同步(调度约束–一个线程等待另一个线程的事件发生)
- 读/开发代码比较困难
- 容易出错
- 使用的信号量已经被另一个线程占用
- 忘记释放信号量
- 不能处理死锁问题
三、管程(moniter)
管程的抽象程度更高,更加容易的来完成相应的同步互斥的问题。
1.定义
管程是包含了一系列的共享变量以及针对这些这些变量函数的一个组合或模块。 其包括:
- 一个锁:指定临界区(确保互斥性,只能有一个线程)
- 0或者多个条件变量:等待/通知信号量用于管理并发访问共享数据
2. 目的:分离互斥和条件同步的关注
- (一开始是完成编程语言的设计,而不是操作系统的设计的,所以其整体上是针对语言的并发机制来完成的)
3. 一般方法
- 收集在对象/模块中的相关共享数据
- 定义方法来访问共享数据