进程之锁机制, 信号机制, 事件机制
锁机制:
l = Lock()
简单记忆:
一把锁配一把钥匙.
拿钥匙, 锁门, l.acuire()
还钥匙, 关门, l.release()
例子:
简单模拟购票:
from multiprocessing import Process,Lock # 导入multiprocessing模块中的 Process方法(创建进程), 和 Lock 方法. import time # 导入时间模块 def check(i): # 定义一个查票的函数 with open('余票') as f: # 打开车票存储文件夹, 读取里面剩余票数. 同一时间每个人查询到的票数是一致的 con = f.read() print('第%s个人查到余票还有%s张'%(i,con)) 将每个人查到的结果打印在屏幕上 def buy_ticket(i,l): # 定义一个购票的函数 l.acquire() # 开启锁机制 , 拿钥匙锁门, 不让其他进程进来,等次次进程执行结束后, 再让其他进程进来. with open('余票') as f: # 打开文件读取票数 con = int(f.read()) time.sleep(0.1) # 睡眠0.1秒, 模拟一个网络延迟. if con > 0: # 当剩余票数大于0的时候, 就能够购票. print('\033[31m 第%s 个人买到票了\033[0m]'%i) con -= 1 # 并将票数减去购买的票的数量 else: # 否则 , 购票失败 print('\033[32m 第%s 个人没买到票了\033[0m]'%i) time.sleep(0.1) # 睡眠0.1秒, 模拟网络延迟 with open('余票','w') as f: 以w的模式写入, 覆盖掉之前的数据. f.write(str(con)) l.release() # 结束本次锁机制. 归还钥匙开门, 让其他进程继续进入 if __name__ == '__main__': l = Lock() # 实例化锁. for i in range(10): # 循环开启10个查票进程 p_ch = Process(target=check, args=(i+1,)) # 实例化是个进程, 并将每次的数字拷贝下来传给子进程 p_ch.start() # 开启查票的子进程 for i in range(10): # 循环开启10个购票进程 p_buy = Process(target=buy_ticket, args=(i+1,l)) # 实例化每次的购票进程, 并将数字, 传递给每次购票进程. p_buy.start() # 开启每个购票进程
简单模拟存取钱:
from multiprocessing import Lock, Value, Process # 导入multiprocessing模块中的 Lock方法, Value方法, Process方法. import time # 导入时间模块 def get_money(num,l): # 定义一个取钱函数 其中参数分别是 父进程中定义的卡内总钱数, 和锁机制. l.acquire() # 拿走钥匙, 锁上门, 不允许其他进程进入 for i in range(100): # 将 将钱分成100份, 每次取一块. num.value -= 1 # 在总钱数上减掉取出来的钱 print(num.value) # 打印剩余钱数 time.sleep(0.01) # 睡眠0.01秒, 模拟网络延迟 l.release() # 还钥匙, 开门, 让其他进程进入 def put_money(num,l): # 定义一个存钱函数, 参数第一个是卡里剩余钱数, 第二个参数为锁机制 l.acquire() # 拿钥匙锁门, 开启锁机制 for i in range(100): # 将钱分成100次存, 每次1块 num.value += 1 # 总钱数累加 print(num.value) # 每次操作完以后剩余钱数 time.sleep(0.01) # 睡眠0.01秒, 模拟网络延迟 l.release() # 还钥匙, 开门, 结束本次锁机制, 让其让进程进入. if __name__ == '__main__': num = Value('i',100) # 在使用的时候, 必须传两个值, 第一个是数据类型, 第二是一个值. #起作用是将变量设为共享变量 l = Lock() # 实例化一个锁, p = Process(target=get_money,args=(num,l)) # 实例化一个取钱进程(子进程) p.start() # 开启取钱进程 p1 = Process(target=put_money,args=(num,l)) # 实例化一个存钱进程(子进程) p1.start() # 开启存钱进程 p.join() # 起到阻塞作用, 将子进程调用过来, 要先执行完子进程以后再去执行父进程. 即将异步变成同步 p1.join() # 起到阻塞作用, 将子进程调用过来, 要先执行完子进程以后再去执行父进程. 即将异步变成同步
# print(num.value) 会获得取钱, 存钱操作完以后, 最后剩余的钱数
补充内容:
一、Value的构造函数:
Value的初始化非常简单,直接类似Value('d', 0.0)即可,具体构造方法为:
multiprocessing.
Value
(typecode_or_type, *args[, lock])。该方法返回从共享内存中分配的一个
ctypes
对象,其中typecode_or_type定义了返回的类型。它要么是一个ctypes类型,要么是一个代表ctypes类型的code。比如c_bool和'b'是同样的,因为'b'是
c_bool的code。
ctypes是Python的一个外部函数库,它提供了和C语言兼任的数据类型,可以调用DLLs或者共享库的函数,能被用作在python中包裹这些库。
*args是传递给ctypes的构造参数
二、Value的使用
对于共享整数或者单个字符,初始化比较简单,参照下图映射关系即可。如i = Value('i', 1), c = Value('c', '0')。
注意,如果我们使用的code在上表不存在,则会抛出:
size = ctypes.sizeof(type_)
TypeError: this type has no size
如果共享的是字符串,则在上表是找不到映射关系的,就是没有code可用。所以我们需要使用原始的ctype类型
例如
from ctypes import c_char_p
ss = Value(c_char_p, 'ss')
ctype类型可从下表查阅
引用自:https://www.cnblogs.com/junyuhuang/p/5539599.html
信号机制:(信号量)一把锁配多把钥匙.
信号(signal)-- 进程之间通讯的方式,是一种软件中断。一个进程一旦接收到信号就会打断
原来的程序执行流程来处理信号。
信号量机制比锁机制多了一个计数器, 这个计数器是用来记录当前剩余几把钥匙的.
当计数器为0时, 表示没有钥匙了, 此时acquire()处于阻塞.
对于计数器来说, 每次acquire一次, 计数器内部就减掉1, release一次, 计数器就加1.
sem = Semaphore(num) # 传入的参数为数字(int类型).
num 代表的十几把钥匙
num.acquire() # 开启信号机制 拿钥匙,锁门
num.release() # 结束信号机制
例子一:
from multiprocessing import Semaphore,Lock
# l = Semaphore(4)
#
# l.acquire()# 拿走1把钥匙,锁上门
# print(123)
# l.acquire()# 拿走1把钥匙,锁上门
# print(456)
# l.acquire()# 拿走1把钥匙,锁上门
# print(789)
# # l.release()
# l.acquire()# 拿走1把钥匙,锁上门
# print(120)
# 表示初始化的时候, 只有四把要是, 每次调用一次,
# 最多进行四次, 指导有人归还钥匙, 才能继续进行
例子二: 进屋
from multiprocessing import Process,Semaphore import time import random def func(i,sem): sem.acquire() # 每开启1次表示其内部计数器就减少1, 要是少一把. print('第%s个人进入小黑屋,拿了钥匙锁上门' % i) time.sleep(random.randint(3,5)) # 每个人进屋以后, 随机停留的时间. print('第%s个人出去小黑屋,还了钥匙打开门' % i) sem.release() # 每次关闭, 向计数器返还1把钥匙 if __name__ == '__main__': sem = Semaphore(5)# 初始化了一把锁5把钥匙,也就是说允许5个人同时进入小黑屋 # 之后其他人必须等待,等有人从小黑屋出来,还了钥匙,才能允许后边的人进入 for i in range(20): # 表示有20个人在排队 p = Process(target=func,args=(i,sem,)) # 实例化一个进程 p.start() # 开启一个进程.
事件机制:
e = Event() # 实例化一个事件机制
常用方法:
e.is_set() 标识
e.set() 将is_set() 设为Ture
e.clear() 将is_set()设置为False
e.wait() 判断is_set的bool值, 如果bool值为Ture, 则为非阻塞, bool值为False, 则为阻塞
事件是通过is_set()的bool值, 去标识e.wait() 的状态, 是否为阻塞状态.
当is_set()的bool值为False的时候, e.wait()的状态是阻塞状态.
当is_set()的bool值为Ture的时候, e.wait()的状态是非阻塞状态.
当使用e.set()的时候, 是把is_set()的bool值变为Ture.
当使用e.clear()的时候, 是把is_set()的bool值变为False.
注意: is_set()的bool值默认为False.
例子:
红绿信号灯:
import time import random def tra(e): '''信号灯函数''' # e.set() # print('\033[32m 绿灯亮! \033[0m') while 1:# 红绿灯得一直亮着,要么是红灯要么是绿灯 if e.is_set():# True,代表绿灯亮,那么此时代表可以过车 time.sleep(5)# 所以在这让灯等5秒钟,这段时间让车过 print('\033[31m 红灯亮! \033[0m')# 绿灯亮了5秒后应该提示到红灯亮 e.clear()# 把is_set设置为False else: time.sleep(5)# 此时代表红灯亮了,此时应该红灯亮5秒,在此等5秒 print('\033[32m 绿灯亮! \033[0m')# 红的亮够5秒后,该绿灯亮了 e.set()# 将is_set设置为True def Car(i,e): e.wait()# 车等在红绿灯,此时要看是红灯还是绿灯,如果is_set为True就是绿灯,此时可以过车 print('第%s辆车过去了'%i) if __name__ == '__main__': e = Event() # 初始化事件机制. triff_light = Process(target=tra,args=(e,))# 实例化信号灯的进程 triff_light.start() # 开启信号灯进程 for i in range(50):# 描述50辆车的进程 if i % 3 == 0: # 当是3的倍数的时候, 睡眠三秒. time.sleep(2) car = Process(target=Car,args=(i+1,e,))# 实例化汽车通过进程 car.start() # 开启汽车通过进程.