进程同步
线程同步的目的
- 不管线程之间的执行如何穿插,其运行结果都是正确的。保证多线程执行下结果的确定性。
- 效率
线程同步:让所有的线程按照一定的规则执行,使其正确性和效率都有迹可循。
同步手段:对线程之间的穿插进行控制
竞争:两个或者多个线程争相执行同一段代码或访问同一资源的现象
临界区:可能造成竞争的共享代码或资源成为临界区
互斥:一次仅有一个人使用共享资源,其他人皆排除在外。
锁
两个基本操作:闭锁和开锁
两个步骤:
1):等待锁达到打开状态
2):获得锁并锁上
锁的基本特性:
- 初始状态为打开
- 进临界值前必须获得锁
- 出临界值前必须打开锁
- 别人持有锁的时候必须等待
信号量
同步原语 && 通信原语,还可以当成锁来使用
semaphore说白了就一计数器。其取值为当前累积的信号数量。
支持两个操作:加法操作Up 和 减法操作Down
Down减法操作:
- 判断信号量的取值,如果信号量 >= 1,则将信号量的值减1,继续往下执行
- 如果信号量<1,在该信号量上等待(线程挂起)
Up加法操作:
- 将信号量的值加1(该操作叫醒一个在该信号量上等待的线程)
- 线程继续执行
注*:虽然Down和Up操作有多个步骤,可是这些步骤是一组原子操作,不可分开执行。
二元信号量:将信号量的取值限制为0和1的两种情况,则获得一把锁,也成为二元信号量。
二元信号量的功能和锁非常相似,Down就是获得锁,而Up就是释放锁。
信号量能够轻而易举的解决生产者和消费者的同步问题
P,V操作
信号量执行的操作:初始化,P和V
P表示信号量值减1,当信号量小于0时,进程置为阻塞态,重新调度。down
V表示信号量加1:Up
管程
基于信号量繁多时,程序编写困难效率低下而提出。
管程(Monitor):监视的是进程或线程的同步操作。
具体来讲,管程就是:一组子程序、变量和数据结构的组合。
一个管程的例子:
1 | begin monitor |
编译器保证只能有一个线程活跃在管程里面。
编译器看到begin monitor
和end monitor
之间时就知道期间的代码需要同步保护。
管程中的同步机制:所用来互斥,条件变量用来控制执行的顺序。