day29-2 进程加锁
进程安全问题
1.当并发的多个任务要同时操作同一个资源,就会造成数据错乱的问题。
多个进程共享同一打印终端
import random
import time
from multiprocessing import Process
def task1():
time.sleep(random.random())
print('task1 run ------')
time.sleep(random.random())
print('task1 stop ------')
def task2():
time.sleep(random.random())
print('task2 run +++++')
time.sleep(random.random())
print('task2 stop +++++')
if __name__ == '__main__':
p1 = Process(target=task1)
p2 = Process(target=task2)
p1.start()
p2.start()
-----------------------------------------------------------------------------
# p1和p2两个子进程并发运行,效率高,但竞争同一打印终端,带来了打印错乱的问题,
task2 run +++++
task1 run ------
task2 stop +++++
task1 stop ------
多个进程共享同一文件,模拟抢票
# 文件db.json内容为:{"count":1},多用户共享同一个文件
import json
from multiprocessing import Process, Lock
def show():
with open('db.json', 'r') as f:
data = json.load(f)
print('\033[31m剩余票数%s\033[0m' % data['count'])
def buy():
with open('db.json', 'r') as f:
data = json.load(f)
if data['count'] > 0:
data['count'] -= 1
time.sleep(0.1) # 模拟写数据的网络延迟
with open('db.json', 'w') as fw:
json.dump(data, fw)
print('\033[31m购票成功\033[0m')
def task():
show()
buy()
if __name__ == '__main__':
lock = Lock()
for i in range(10): # 模拟并发10个客户抢票
p = Process(target=task)
p.start()
-----------------------------------------------------------------------------
# 由于在多进程并发执行,共享同一文件,竞争数据造成错乱,出现一张票多人购票成功的现象
2.解决方法:将并发操作公共资源的代码,由并发变为串行,解决安全问题,但是牺牲了效率
-
串行方式1:直接使用join()函数
缺点:将任务中的所有代码全都串行,此时还不如不要开进程。多个进程之间原本公平竞争,join是强行规定了执行顺序
-
串行方式2:互斥锁
原理:将要操作的公共资源的代码锁起来,以保证同一时间只能有一个进程执行这部分代码
# 模拟抢票
from multiprocessing import Process, Lock
import time, json
def search():
dic = json.load(open('db.json'))
print('\033[43m剩余票数%s\033[0m' % dic['count'])
def get():
dic = json.load(open('db.json'))
time.sleep(0.1) # 模拟读数据的网络延迟
if dic['count'] > 0:
dic['count'] -= 1
time.sleep(0.2) # 模拟写数据的网络延迟
json.dump(dic, open('db.json', 'w'))
print('\033[43m购票成功\033[0m')
def task(lock):
# 仅将部分代码串行
search()
lock.acquire() # 加锁
get()
lock.release() # 解锁
if __name__ == '__main__':
lock = Lock() # 创建锁,必须保证锁只有一把
for i in range(10): # 模拟并发10个客户端抢票
p = Process(target=task, args=(lock,))
p.start()
锁其实只是给执行的代码加了限制,本质上是一个标志True或False。被锁住的代码越多,锁的粒度越大,效率越低。所以为提高进程的效率,应尽量锁住越少的代码