进程同步锁(multiprocess.Lock)
前面我们实现了进程的并发, 进程之间的数据是不共享的, 但是他们可以共享同一个文件(硬盘空间), 或者是同一个打印空间, 然而在共享的同时也带来了问题 : 进程的运行不是同时进行的, 它们没有先后顺序, 一旦开启也不受我们的限制, 当多个进程使用同一份数据资源时, 就会引发数据安全或者数据混乱问题
1.什么是互斥锁
我们打个简单的比方, 公司里的一台打印机, 每个人都可以使用, 但同事只能有一个人在使用, 不然就会造成打印错乱; 又比如合租房的卫生间, 合住的同伴都可以使用卫生间, 但每次只能一个人进去, 进去之后门就锁上了(相当于加锁 Lock( ).acquire
( )), 出来之后开门, 其他人又可以使用卫生间了(相当于解锁Lock( ).release( )
)
🍓余票文件 "aaa.json"
{"count": 1} # 剩一张票
🍓模拟多个人抢票
# coding:utf-8
from multiprocessing import Process
import os,time,json
def check(): # 先查票
time.sleep(1) # 模拟网络延迟
with open("aaa.json")as f:
dic = json.load(f)
print(f"剩余票数 : {dic['count']}")
def get(): # 查完之后开始抢
time.sleep(1) # 模拟网络延迟
with open("aaa.json")as f:
dic = json.load(f)
if dic["count"] >0:
dic["count"] -= 1
time.sleep(1) # 模拟网络延迟
with open("aaa.json","w")as f2: # 抢完之后修改数据并提交到服务端
json.dump(dic,f2)
print(f"用户 : {os.getpid()} 抢票成功")
else:
print(f"用户 : {os.getpid()} 抢票失败")
def run():
check()
time.sleep(1) # 模拟网络延迟
get()
if __name__ == "__main__":
for i in range(4):
p = Process(target=run)
p.start()
'''输出
剩余票数 : 1
剩余票数 : 1
剩余票数 : 1
剩余票数 : 1
用户 : 13116 抢票成功
用户 : 2364 抢票成功
用户 : 1796 抢票成功
用户 : 6228 抢票成功
'''
打印的结果发现只有一张票, 但是四个人都抢成功了, 这就非常不合理,造成了数据混乱
# coding:utf-8
from multiprocessing import Process,Lock
import os,time,json
def check(): # 先查票
time.sleep(1) # 模拟网络延迟
with open("aaa.json")as f:
dic = json.load(f)
print(f"剩余票数 : {dic['count']}")
def get(): # 查完之后开始抢
time.sleep(1) # 模拟网络延迟
with open("aaa.json")as f:
dic = json.load(f)
if dic["count"] >0:
dic["count"] -= 1
time.sleep(1) # 模拟网络延迟
with open("aaa.json","w")as f2: # 抢完之后修改数据并提交到服务端
json.dump(dic,f2)
print(f"用户 : {os.getpid()} 抢票成功")
else:
print(f"用户 : {os.getpid()} 抢票失败")
def run(lock):
check()
time.sleep(1) # 模拟网络延迟
lock.acquire() # 在抢票环节加锁
get()
lock.release() # 抢完后解锁
if __name__ == "__main__":
lock = Lock()
for i in range(4):
p = Process(target=run,args=(lock,))
p.start()
'''输出
剩余票数 : 1
剩余票数 : 1
剩余票数 : 1
剩余票数 : 1
用户 : 432 抢票成功
用户 : 2636 抢票失败
用户 : 7772 抢票失败
用户 : 1272 抢票失败
'''
加锁之后, 一张票只有一个人能抢成功, 其实就是让抢票这个局部环节变成了串行, 谁抢到了就谁用, 牺牲了效率, 提升了数据安全性
2.总结
- 以上加锁的操作方法可以保证多个进程修改同一份数据时保证数据的安全性, 即串行的修改, 但是也带来了一些问题 :
1、共享的数据基于文件, 文件又属于硬盘, 效率就比较低
2、需要自己加锁和解锁操作, 这是一件非常危险的操作, 如果忘记解锁程序就停在原地
- 因此我们就需要一种兼顾效率以及能自动帮我们处理锁的问题的这种介质
🍑需求
1、多个进程共享同一块内存数据, 实现高效率
2、找到一个能帮我们处理好锁的问题的机制 : multiprocessing模块为我们提供了IPC通信机制:管道和队列
🍑介质
1、管道和队列, 基于内存中的空间存放数据
2、队列是基于管道和锁实现的, 可以让我们从复杂的锁问题中解脱出来
ps : 我们应该尽量避免使用共享数据, (比如一个文件的传递应该将文件保存到硬盘, 在管道中放的应该是一个路径, 而不应该是一个完整的文件), 尽可能使用消息传递和队列, 避免处理复杂 的同步和锁问题, 而且在进程数目增多时, 往往可以获得更好的可扩展性