转载

轻松理解多线程同步基础概念

16 Jan 2016

Semaphore(信号量)

Semaphore是多线程编程时一个很重要的概念,概念本身并不复杂,但要做到正确使用却不容易。这里我们从面向对象的角度来理解这个概念。现实生活中的任何对象或实体,我们都可以用class来描述它。Semaphore也不例外,如果用class定义应该是这样:

轻松理解多线程同步基础概念

Semaphore对象里包含一个count值和一个队列对象,另外有两个对外public的方法,wait()和signal(),需要特别注意的事,count值代表的资源数量是不能为负的。为了理解这些属性和方法,我们可以类比一个现实生活中的例子。大家去餐厅吃饭,假设这个餐厅有10个座位,有20个吃货随机出发去这个餐厅吃饭,那么对应关系是这样的:

  • count = 10,10个座位。
  • queue,餐厅位置有限,为了避免混乱,餐厅肯定会吃货们排队。
  • wait(),吃货到了餐厅找服务员要位置点餐,这个行为就是wait。
  • signal(),吃货吃完了买单离开位置,这个行为就是signal。

这其实是一个信号量应用的典型场景,这里关键在于正确理解wait和signal发生时都有哪些细节步骤。用代码来描述大概是这样:

轻松理解多线程同步基础概念

wait

具体到餐厅到例子,20个人随机出发去餐厅吃饭,有10个人先到,然后挨个执行wait。前10个人执行wait的时候是有位置的,所以count>0,这10个人每人都消耗掉一个座位开始吃饭。到第11个人到了都时候,count==0,没有位置了,所以被suspend,开始加入排队都队列等待。后续所有人都慢慢的到来,但和第11个人一样,都只能排队。

signal

过了一段时间之后,有个人吃好结账离开了餐厅。这时候如果没有人在排队,位置数量count ++,没有其它事情发生。但如果有人在排队,比如上面的情况,有10个人在等待位置,餐厅会把排在第一个的人安排到刚才空出来的位置,count值没有变化,但队列的人少了一个。

特别注意

对于wait和signal还有两点需要特别注意。也是平时我们使用semaphore时比较容易产生bug的地方。

  • wait和signal都是原子操作。可以简单理解为上面代码里wait(),signal()两个函数都是加锁的。这个特性其实让semaphore的行为变得更简单清晰。大家想象,如果到餐厅的10个人是同时到达的,但不是依次询问餐厅是否有位置,而是10张嘴同时说话,同时找餐厅要位置,显然情况会变得复杂不好处理。
  • wait或者signal调用的顺序是不确定的。上面的例子中每个人都是随机时间出发,到达餐厅的顺序也是随机的,并不一定先出发的就先到。同理每个人吃饭的时间长短也不一定,有人快有人慢,所以吃好离开餐厅的时间点也是随机的。这里每个人都代表一个线程,因为操作系统线程调度策略导致到底哪个线程先执行也是不确定的。
原文  http://music4kid.github.io/operating system/2016/01/16/threading-lock/
正文到此结束
Loading...