参考LittleBookOfSemaphores中的Morris’s Solution
原理及代码实现
设置锁属性如下
我们设置有三个线程waiting空间:room1,room2,room3
其中room3是隐式设置的,里面同一时间只能有一个线程,也可以说是互斥的
初始化锁如下
我们的原理如下
- 先将一段时间内发出获取锁请求的线程都放入
room1
,也就是阻塞在图中代码第48行 - 由于信号量s1初始值为1,第一个进入
room1
的线程可以继续执行48行之后的内容,它可以进入room2
,也就是被阻塞在图中代码第67行的位置。
它在room2
中进行了一个操作,就是判断自己之后是否还有线程在room1
中被阻塞,若有的话,随机唤醒一个进入room2
,若没有了,就通知room2
中的线程可以依次进入room3
。 - 当
room1
中的线程全部进入room2
,最后一个线程发出s2的post,room2
中的随机一个线程被唤醒,在图中代码第69行开始继续执行,离开room2
(进入room3
),执行线程的主体程序,然后释放锁 - 在释放锁时,线程会回头判断身后
room2
中是否还有线程被阻塞,若有,随机唤醒一个进入room3
,若没有了,就通知这段时间内被阻塞在room1
门外也就是图中代码第42行的线程可以依次进入room1
。
获取锁
释放锁
通过这一种方式,我们一批一批处理请求获得锁的线程,缓解了有线程从一开始就被阻塞,直到几乎所有线程都执行完才轮到执行的饥饿问题。
效果验证
为了证明确实较好地解决了饥饿问题,我设置一个数组starve,用来存储各个线程的饥饿值。
- 当有一个线程执行acquire锁的时候,该线程的饥饿值+1,且所有线程的饥饿值乘2
- 当线程执行release锁的时候,对应饥饿值清0。
- 当我们执行room3中的线程主体部分时,打印所有线程的当前饥饿情况,并累加到一个sum值中,执行全部线程结束后打印总饥饿度sum。同时我们也通过maxx变量记录最大饥饿值。
执行结果:
总饥饿值为77208,最大饥饿度为1024
这样看好像还看不出什么,但是我设置了一个会产生饥饿的锁进行比对
简单的单信号量存在饥饿问题的互斥锁:
也是一样的饥饿算法
最大饥饿度就达到夸张的1073741824!!
两者相比就能看出优化效果之好了