PV操作原语和信号量sem是计算机操作系统进程和线程同步的核心手段,虽然说起来只有句话,但有几个点非常容易引起模糊。先把PV操作的说明如下:
P操作原语:
1. sem 减1
2. 若sem 大于等于0,线程继续执行.
3. 若sem < 0 ,线程进入阻塞队列.
V 操作原语:
1. sem加1
2. 若sem 大于 0, 线程继续执行
3. 若sem 小于等于0,唤醒阻塞队例的线程
问题1.原语概念
教科书上一般定义是不允许发生中断,一段必须连续执行的指令。在Dijkstra发明原语概念的时代,CPU应该都是单核的,多线程是单个CPU分时间片执行,所有只要是保证是” 连续执行”,就不会发生执行一个线程P操作的sem减1后,另一个线程V操作sem 加1。这意味着可以在现有的程序层面就可以实现多线程的同步而不需要别的指令或硬件。但在现在的多核时代,完全有可能在一个核上执行P操作sem减1后,另一个核上执行V操作sem加1,修改相同的地址空间,从而破坏sem的状态. 这方面.NET和Java都提供了volatile关键字,估计是添加的新的指令来保证,不过还好,这是操作系统操心的事情,操作系统保证原语概念的完整性。
问题2. 信号量
信号量sem在PV操作里具有三元性质.
让我们来看一个具体的实例: 假设现在的执行场景是有三个线程A,B,C进入一个信号量为1的临界资源,
1. 当线程A进入时,执行P操作, sem=0,线程A继续执行.
2. 当线程B进入时,线程A假设仍在占用临界资源, B执行P操作,sem = -1,B进入等待队列。
3. 当线程C进入时,线程A假设仍在占用临界资源, C执行P操作,sem = -2,C进入等待队列
通过以上的步骤可以发现:
1) 当sem > 0 时,表示可用的临界资源的数量
2) 当sem = 0时,表示无可用的资源,也没有阻塞的线程
3) 当sem <0 时,sem的绝对值表示阻塞队列的线程数
问题3.对V操作2,3步骤的解释
V操作的第2步,sem > 0 ,线程继续执行.有些人这个地方不解,sem > 0不是代表有资源可用吗?为什么不是唤醒其它的线程。其实正是sem >0 ,sem当前的意义是有资源可用,而没有阻塞的线程,所有根本不需要唤醒.
V操作的第3步,sem 小于等于0, 唤醒阻塞队例的线程. sem < 0很多人直觉认识都是没资源可用了,干吗还唤醒阻塞队列的线程(包括我)。通过问题2的分析,在这个时候sem代表的是阻塞队列的线程数 ,所以需要唤醒. 让我们接着问题2的步骤.
4) 线程A资源使用完,执行V操作,sem = -1, 唤醒阻塞队列的线程(假设FIFO),B线程进入临界资源开始执行,注意,B不会再执行P操作,因为进入阻塞队列前已执行.P,V操作必须成对执行。
5) B使用完资源后,执行V操作,sem = 0; 唤醒阻塞队列的线程,C线程进入临界资源开始执行,C也不会再执行P操作
6) C使用完资源,执行V操作, sem = 1
一点小总结,希望有用。