1. 锁: 同一时间允许一个进程上一把锁 就是Lock
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,速度会变得稍慢,但牺牲速度却保证了数据安全。
互斥锁Lock : 互斥锁就是进程的互相排斥,谁先抢到资源,谁就上锁改资源内容,为了保证数据的同步性
注意:两个或以上的锁一起上,不开锁,会造成死锁.上锁和解锁是一对.
1.1 lock的基本用法:
from multiprocessing import Process,Lock # 创建一把锁: lock = Lock() # 上锁: lock.acquire() # lock.acquire() 如果上多个锁不解锁,就会变成死锁 # 解锁: lock.release()
1.2 模拟12306抢票
# 模拟12306抢票软件 from multiprocessing import Process,Lock import json,time lock = Lock() # 读取,更新票数 def wr_ticket(sign,dic=None): if sign == "r": with open("ticket.txt",mode="r",encoding="utf-8") as fp: dic = json.load(fp) return dic elif sign == "w": with open("ticket.txt",mode="w",encoding="utf-8") as fp: json.dump(dic,fp) # 抢票方法 def get_ticket(person): dic = wr_ticket("r") if dic["count"] > 0: print("%s可以回家了"%(person)) dic["count"] -= 1 wr_ticket("w",dic=dic) else: print("%s回不了家了!"%(person)) def run(person,lock): dic = wr_ticket("r") print("%s在查询余票,余票%d张"%(person,dic["count"])) lock.acquire() get_ticket(person) # 加锁让进程从异步变成了同步 lock.release() if __name__ == "__main__": lst = ["bob","alice","jack","rose"] for i in lst: p = Process(target=run,args=(i,lock)) p.start()
1.3 信号量Semaphore: 本质上也是锁,可以设定锁的数量
from multiprocessing import Process,Semaphore import time def ktv(person,sem): sem.acquire() print("%s开始唱歌了"%(person)) time.sleep(0.3) print("%s唱完回家了"%(person)) sem.release() if __name__ == "__main__": sem = Semaphore(3) # 设置为一次允许3个进程进去 for i in ["bob","jack","rose","lisa"]: p = Process(target=ktv,args=(i,sem)) p.start()
2.
程和进程之间的数据彼此隔离,但是可以通过socket互相发消息 e = Event()生成事件对象e
e.wait()动态给程序加阻塞 , 程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False]
如果是True 不加阻塞,如果是False 加阻塞
控制这个属性的值
set()方法 将这个属性的值改成True
clear()方法 将这个属性的值改成False
is_set()方法 判断当前的属性是否为True (默认上来是False)
2.1 模拟交通灯效果
# 模拟红绿灯效果 from multiprocessing import Process,Event import random,time def tarffic_light(e): print("红灯亮") while True: if e.is_set(): # 默认是False time.sleep(0.5) print("红灯亮") e.clear() else: time.sleep(1) print("绿灯亮") e.set() def car(e,i): if not e.is_set(): # 与交通灯相对应,这里应该是停车状态 print("车%s要等待"%(i)) e.wait()
print("车%s可以走了"%(i)) if __name__ == "__main__": e = Event() p1 = Process(target=tarffic_light,args=(e,)) p1.start() for i in ["红色","蓝色","黑色","白色","紫色","棕色","黄色","银色"]: time.sleep(random.randrange(0,2)) p2 = Process(target=car,args=(e,i)) p2.start() # 将红绿灯变成守护进程 from multiprocessing import Process,Event import random,time def tarffic_light(e): print("红灯亮") while True: if e.is_set(): # 默认是False time.sleep(0.5) print("红灯亮") e.clear() else: time.sleep(1) print("绿灯亮") e.set() def car(e,i): if not e.is_set(): # 与交通灯相对应,这里应该是停车状态 print("车%s要等待"%(i)) e.wait()
print("车%s可以走了"%(i)) if __name__ == "__main__": e = Event() lst = [] p1 = Process(target=tarffic_light,args=(e,)) p1.daemon = True p1.start() for i in ["红色","蓝色","黑色","白色","紫色","棕色","黄色","银色"]: time.sleep(random.randrange(0,2)) p2 = Process(target=car,args=(e,i)) p2.start() lst.append(p2) for j in lst: j.join() print("全部通过")
3. 进程间的通信:
实现进程之间通信的两种机制:管道 Pipe和队列 Queue
put() 存放
get() 获取 用get()获取值时,如果队列中没有值了,程序会阻塞在这里
get_nowait() 拿不到报异常
put_nowait() 非阻塞版本的put 只能在Windows里用,Linux里用不了
q.empty() 检测是否为空 (了解)
q.full() 检测是否已经存满 (了解)
3.1 基本用法
from multiprocessing import Process,Queue q = Queue(2) q.put(1) q.put(2) # q.put(3) # 如果队列中只设定放2个值,当放入第三个值时,程序会阻塞,在等别的程序将值拿走,再放第三个值 res = q.get() print(res) q.get() q.get() # 同理,如果队列中只设定放2个值,当取出第三个值时,程序会阻塞,在等队列中有值取出再放入第三个值再打印
3.2 进程之间通过队列交换数据
from multiprocessing import Process,Queue def func1(q): res1 = q.get() print(res1+"111") q.put("午餐") if __name__ == "__main__": q = Queue() p = Process(target=func1,args=(q,)) p.start() q.put("早餐") p.join() res2 = q.get() print(res2+"222") # 早餐111 # 午餐222
3.3 生产者和消费者模型
from multiprocessing import Process,Queue # 消费者 def consumer(q,name): while True: food = q.get() print("%s吃了%s"%(name,food)) # 生产者 def producer(q,food,name): print("%s生产了%s"%(name,food)) q.put(food) if __name__ == "__main__": q = Queue() p1 = Process(target=consumer,args=(q,"bob")) p1.start() for i in ["包子","凉皮","辣条","排骨汤","腊肠"]: p2 = Process(target=producer,args=(q,i,"jack")) p2.start() # 此时程序并未停止,因为生产者模型里面的get没取到值,阻塞了程序 # 优化:发送None from multiprocessing import Process,Queue # 消费者 def consumer(q,name): while True: food = q.get() if food == None: break print("%s吃了%s"%(name,food)) # 生产者 def producer(q,food,name): print("%s生产了%s"%(name,food)) q.put(food) if __name__ == "__main__": q = Queue() lst = [] p1 = Process(target=consumer,args=(q,"bob")) p1.start() for i in ["包子","凉皮","辣条","排骨汤","腊肠"]: p2 = Process(target=producer,args=(q,i,"jack")) p2.start() lst.append(p2) for j in lst: j.join() q.put(None) # 但是发送None的话,每加一个生产者或者消费者进程,都需要发送一个None,太过麻烦
3.4 生产者和消费者模型最终优化版
JoinableQueue:
put 一次 每存放一个值,队列计数器加1
get 一次 通过task_done让队列计数器减1
join 函数,会根据队列中的计数器来判定是阻塞还是放行
如果计数器变量是0,意味着放行,其他情况阻塞
from multiprocessing import Process,JoinableQueue import time,random # 生产者 def producer(jq,name,food): time.sleep(random.uniform(0.1,0.9)) print("%s生产了%s"%(name,food)) jq.put(food) # 消费者 def consumer(jq,name): while True: food = jq.get() time.sleep(random.uniform(0.1,0.9)) print("%s吃了%s"%(name,food)) jq.task_done() if __name__ == "__main__": lst = [] jq = JoinableQueue() p1 = Process(target=consumer,args=(jq,"bob")) p1.daemon = True p1.start() for i in ["包子","凉皮","辣条","排骨汤","腊肠"]: p2 = Process(target=producer,args=(jq,"jack",i)) p2.start() lst.append(p2) for j in lst: j.join() jq.join() print("收工,回家睡觉")
4. Manager: 用于进程之间的数据共享(列表和字典)
from multiprocessing import Manager,Process,Lock def func1(date,lock): lock.acquire() date["count"] -= 1 lock.release() def func2(date,lock): lock.acquire() date[0] += 1 lock.release() if __name__ == "__main__": m = Manager() lock = Lock() dic1 = m.dict({"count":200}) # 字典形式 date2 = m.list([1,2]) # 列表形式 lst = [] for i in range(10): p1 = Process(target=func1,args=(dic1,lock)) p2 = Process(target=func2, args=(date2,lock)) lst.append(p1) p1.start() p2.start() for j in lst: j.join() print(dic1) print(date2) # {'count': 190} # [11, 2] 若不加锁,可能导致数据错误