一、介绍为什么要用进程锁
我们首先看一个例子:
Value 是一个内存共享模块,即使得主进程和子进程共享一块内存…
关于Value共享整数或者单个字符,用 Value(‘i’, 1), Value(‘c’, ‘1’)即可。
如果要是共享字符串,则要导入如下,用 Value(c_char_p, ‘xxx’)即可。
from ctypes import c_char_p
下面先看一个例子,银行存取钱的程序,生活中,取钱与存钱如果要是同时在两个ATM机操作,那么会带来怎样的结果,比如银行卡有一完块钱,两个人同时操作,一个人取九千块,一个人存一万块,那么结果究竟是怎样的呢,然而,现实是存取钱是不可能让两个进程同时操纵数据,否则会带来数据的混乱,下面从这个小例子来解释:
from multiprocessing import Process,Value,Lock
import time
def get_money(num):#取钱
for i in range(100):#取一百块钱
num.value -= 1
time.sleep(0.01)
def save_money(num):#存钱
for i in range(100):#存一百块钱
num.value += 1
time.sleep(0.01)
if __name__ == '__main__':
num = Value('i',100)#内存共享,主进程和子进程内存共享
p = Process(target=get_money,args=(num,L))
p.start()
p_1 = Process(target=save_money,args=(num,L))
p_1.start()
p.join()
p_1.join()
print(num.value)
结果运行一下会发现,结果是不固定的,那么究竟是怎么回事呢?
原来我们同时开启了两个进程,一个进行取钱操作,一个进行存钱操作,操作的数据是同一个数据num,两个进程都要被CPU光顾才能进行操作,而这两个进程的执行是依靠时间片轮转来轮流操作,而CPU内部执行如下赋值语句并不是这么简单
i = 3
i += 1
而是通过
tmp = i 【1】
tmp = tmp + 1 【2】
i = tmp 【3】
所以当某次程序执行到i = 199【1】时,tmp = 200【2】,时间片用完了,转到另一个取钱进程,假如取钱进程只执行了一次使得i = 198,时间片刚好用完,转到存钱进程,继续上次没有完成的操作,i = tmp = 200【3】 那么就将夹在其中的取钱进程给覆盖掉了。
所以为了防止共享内存导致的数据混乱,所以要用进程锁,但也必然会带来效率的降低。
进程锁加上如下:
from multiprocessing import Process,Value,Lock
import time
def get_money(num,L):
L.acquire()
for i in range(100):
num.value -= 1
time.sleep(0.01)
L.release()
def save_money(num,L):
L.acquire()
for i in range(100):
num.value += 1
time.sleep(0.01)
L.release()
if __name__ == '__main__':
num = Value('i',100)
L = Lock()
p = Process(target=get_money,args=(num,L))
p.start()
p_1 = Process(target=save_money,args=(num,L))
p_1.start()
p.join()
p_1.join()
print(num.value)
还可以看一个抢票的小程序:
from multiprocessing import Process,Lock
import time
def check(i,l):
l.acquire()#拿钥匙
with open('余票') as f:
con = f.read()
print('余票还有{}'.format(con))
l.release()#还钥匙
def buy_ticket(i,l):
l.acquire()
with open('余票') as f:
con = int(f.read())
if con > 0:
print('\033[31m 第{}个人买到票了\033[0m'.format(i))
con -= 1
else:
print('\033[32m 第{}个人没有买到票\033[0m'.format(i))
with open('余票','w') as f :
f.write(str(con))
l.release()
if __name__ == '__main__':
l = Lock()
for i in range(10):
p_ch = Process(target=check,args=(i+1,l))
p_ch.start()
for i in range(10):
p_buy = Process(target=buy_ticket,args=(i+1,l))
p_buy.start()
二、进程锁只能拿钥匙开锁,还钥匙加锁,有没有可以多配几把钥匙的方法,下面介绍信号机制
开启20个子进程,一次性只有三个进程能运行函数func ,sem.acquire() 是拿上钥匙,sem.release()是还钥匙 拿上钥匙开锁,还钥匙后别的进程才能进入。
import time
import random
from multiprocessing import Process
def func(i,sem):
sem.acquire()
print('\033[31m第{}进入小黑屋,拿了钥匙锁上门\033[0m'.format(i))
time.sleep(random.randint(1,5))
print('第{}个人出去了小黑屋,还了钥匙上门'.format(i))
sem.release()
if __name__ =='__main__':
sem = Semaphore(3)#初始化几把钥匙
for i in range(20):
p = Process(target= func,args=(i,sem))
p.start()
比如第一次进程1,2,4执行程序,接着3,7,9执行…
三、那有没有根据事件进行开关锁的呢,也是有的。
关于Event 模块的使用如下:
from multiprocessing import Event
e = Event()
事件是通过is_set()的bool值,去标识e.wait()的阻塞状态
当is_set()的bool值为False时,e.wait()是阻塞状态
当is_set()的bool值为True时,e.wait()是非阻塞状态
当使用set()时,是把is_set的bool值变为True
当使用clear()时,是把is_set的bool值变为False
下面介绍一下信号灯程序:
from multiprocessing import Event,Process
import time
def traffic(e):
'''信号灯'''
while 1:
if e.is_set():
time.sleep(5)
print('\033[33m 红灯亮!\033[0m ')
e.clear()
else:
time.sleep(5)
print('\033[32m 绿灯亮!\033[0m')
e.set()
def Car(i,e):
e.wait()
print('第{}辆车经过!'.format(i))
if __name__== '__main__':
e = Event()
triff_light = Process(target=traffic,args=(e,))
triff_light.start()
for i in range(50):
car = Process(target=Car,args=(i+1,e))
car.start()
下图是上段程序的图解:
最后,分享知识,造福人民,为实现我们中华民族伟大复兴!祝大家越来越好!