信号量(英语:semaphore)又称为信号标,
是一个同步对象,用于保持在0至指定最大值之间的一个计数值。
当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;
当线程完成一次对semaphore对象的释放(release)时,计数值加一。
当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。
semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态.
信号量的概念是由荷兰计算机科学家艾兹赫尔·戴克斯特拉(Edsger W. Dijkstra)发明,广泛的应用于不同的操作系统中。
在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,
未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来。
如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);
如果信号量只有二进制的0或1,称为二进制信号量(binary semaphore)。
以停车场的运作为例。
假设停车场只有三个车位,开始三个车位都是空的。
这时同时来了五辆车,看门人开闸允许其中三辆直接进入,
剩下的车则必须在入口等待,后续来的车也在入口处等待。
这时一辆车想离开停车场,告知看门人,打开闸门放他出去,
看门人看了看空车位数量,然后看门人才让外面的一辆车进去。
如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,
看门人起的就是信号量的作用。
计数信号量具备两种操作动作,
V(signal())
P(wait())
(即部分参考书常称的“PV操作”)。
V操作会增加信号标S的数值,P操作会减少它。
运作方式:
初始化,给与它一个非负数的整数值。 如车位3个
运行P(wait()),信号标S的值将被减少。 减少一个车位
企图进入临界区段的进程,需要先运行P(wait())。 进入前,空车位数减1再放它进去
当信号标S减为负值时,进程会被挡住,不能继续; 没车位了,就拦住后面想进入的车
当信号标S不为负值时,进程可以获准进入临界区段。 有车位了,那就进去
运行V(signal()),信号标S的值会被增加。 告知,有车离开停车场了,显示空车位加1
结束离开临界区段的进程,将会运行V(signal())。 车离开前,会告诉看门人,放他出去
当信号标S不为负值时,先前被挡住的其他进程,将可获准进入临界区段。 有空车位,可以让其它车进去
python统一了所有的命名
使用与线程锁同样的方法命名小好和释放资源
acquire方法
消耗资源加1 空车位减1
release方法
释放资源加1 空车位加1
创建BoundedSemaphore类实例
才可以使用信号量semaphore
通过该类的构造方法传入计数器的最大值空车位总数
from threading import BoundedSemaphore
Max =3
s =BoundedSemaphore(Max)
print(s._value)#输出计数器的值
s.acquire()#消耗1个资源
s.acquire()
s.acquire()
print(s._value)#输出计数器的值 这时输出的是0 也可以表示为False 当设定条件时,可以使用False来作为条件判断
# s.acquire()#已经没有资源了,再减就一直处于等待状态,除非设定了其他资源在执行完毕后并释放 这样才能继续消耗
s.release()#释放1个资源
s.release()
s.release()
# s.release()#已超过资源设定的最大值了,再加就抛出异常 相当于停车场一共才3个车位,怎么会显示有4个车位呢?
print(s._value)#输出计数器的值
一个4S店有5个车位,进货,再卖车的示例:
from atexit import register
from random import randrange
from threading import BoundedSemaphore,Lock,Thread
from time import ctime,sleep
lock =Lock() #创建线程锁
Max =5#总共车位数量
Parking = BoundedSemaphore(Max)#信号量 值为5
#拉车进货 每次进车动作时执行时,不能卖车
def CarInto():
lock.acquire()#获取线程锁
print("车位:",Parking._value,end="")
print("准备拉车入库....",end="")
# 如果消耗资源成功,车辆进入,否则显示车已经足够
try:
Parking.acquire()#消耗1资源
except ValueError:
print("车位已经满了,车位:",Parking._value)
else:
print("车辆进入,车位:",Parking._value)
lock.release()#释放线程锁
#卖车出去 每次卖车动作在执行时,不能进车
def CarLeave():
lock.acquire()# 获取线程锁
print("车位:",Parking._value,end="")
print("准备卖车出库....",end ="")
# 当资源释放成功,车辆出去,否则显示没有车了
try:
Parking.release()#增加1资源
except ValueError:
print("没有车了,车位:",Parking._value)
else:
print("车辆卖出,车位:",Parking._value)
lock.release() #释放线程锁
#拉车进库 loops为一个数值 #产生[loops]次拉车入库动作
def PullIn(loops):
for i in range(loops):
CarInto()
sleep(3)
#卖车出去 loops为一个数值 #产生[loops]次卖车出库动作
def ThrowOut(loops):
for i in range(loops):#产生2-5次卖车动作
CarLeave()
sleep(3)#每个卖车动作 休眠1-3秒
def main():
print("开始营业:",ctime())
nloops = randrange(4,7)#产生一个4-6的随机数
print("设定车位:%d"%Max)
print('随机值为',nloops)#卖出车的最高值
# 一般情况下会线执行进货再卖车
# 进货线程 实例
# [拉车线程] [随机值]
# 启动
Thread(target=PullIn,args=(nloops,)).start()
#卖车线程 实例
# [卖车线程] [再次定义一个随机值]
# 启动
Thread(target=ThrowOut,args=(randrange(nloops,nloops+5),)).start()
#添加到程序退出前 显示营业结束
@register
def exit():
print("营业结束",ctime())
if __name__=="__main__":
main()