day 33 进程锁,进程之前互相通信,生产者消费者模型
进程锁 -multiprocess.Lock
锁的应用场景
对数据库的写操作必须是串行,读操作可以并行,没办法并发写的操作,如果并发对数据库写的操作会导致数据不一致性.
import time import os import json from multiprocessing import Process,Lock #用抢票系统来模拟一个进程锁 def search_piao(name): with open("ticket") as f: #with open 打开一个文件默认的模式是读 ticket = {"count": 0} piao_dic = json.load(f) #给字典赋予名字,利用名字操作字典 time.sleep(0.1) print("%s查询了余票,还剩%s张" % (name,piao_dic["count"])) def get_piao(n,lock): with open("ticket") as f: #买票之前还需要再查询一遍还有几张票 piao_dic = json.load(f) time.sleep(0.1) #加上等待0.1秒是为了模仿生产环境中网络延迟,并行的机制会默认处理下一个用户,这样每个人查询的数据就不一致了 if piao_dic["count"] > 0: #先判断没有有票,如果有票就可以买 print("%s查询了余票,还剩%s张" % (n,piao_dic["count"])) piao_dic["count"] -= 1 print("%s购票成功,还剩下%s张票" % (n,piao_dic["count"])) time.sleep(0.1) with open("ticket",mode="w") as f: json.dump(piao_dic,f) def buy_paio(name,lock): search_piao(name) lock.acquire() get_piao(name, lock) lock.release()
#上下文关联写法
def use_system(name,lock):
search_ticket(name)
with lock:
buy_ticket(name)
if __name__ == '__main__': lock = Lock() for i in range(10): # p = Process(target=search_piao,args=("alex"+str(i),),name="搜索") # p.start() p1 = Process(target=buy_paio,args=("alex"+str(i),lock,),name="买票") p1.start()
进程队列-进程间通信(IPC(Inter-Process Communication))
认识进程队列:
进程之间的数据安全
对列最鲜明的特征就是先进先出
进程之间的数据本身是隔离的,但是用"Queue"方法可以做到多进程之间的互相通信
队列底层: 是管道+锁实现的
管道 ""Pipe"" 没有锁,数据是不安全的,""并发""效率高
Queue队列的集中方法:
q = Queue()
q.empty() 判断队列是否为空(由于网络延迟的原因,判断的都不准确,不建议使用)
q.full() 判断队列是否为满(由于网络延迟的原因,判断的都不准确,不建议使用)
q.qsize() 队列中数据的个数(由于网络延迟的原因,判断的都不准确,不建议使用)
q.get_nowait() 拿取数据时超过队列池的限制数量的时候不会卡主,但是程序会抛出一个异常"queue.Empty".
q.put_nowait() 存数据时超过队列池的限制数量的时候不会卡主,同样程序也会抛出一个异常"queue.Empty",生产环境不建议使用,一位这样会造成数据丢失.
子进程之间的互相同通信
def bar1(q): q.put("aaa") q.put("aaa") #超过数据池的限制,放第二条数据的时候会卡住 def bar2(q): print("-->",q.get()) print("-->",q.get()) #超过数据池的限制,取第二条数据的时候同业也会卡住 if __name__ == '__main__': q = Queue(1) #数据池,控制存放数据和拿取数据的数量 p = Process(target=bar1,args=(q,)) p.start() p2 = Process(target=bar2,args=(q,)) p2.start()
#总结:当队列池限制存放数据数量时,往里面put或者get超过限制数量程序都会卡主
#子进程并发通信 import time def bar(q): q.put("aaa") q.put("bbb") time.sleep(2) print("in bar get" ,q.get()) def bar2(q): print("in bar1 get",q.get()) if __name__ == '__main__': q = Queue() p = Process(target=bar,args=(q,)) p.start() p1 = Process(target=bar2,args=(q,)) p1.start() #结果 in bar1 get aaa in bar get bbb
生产者消费者模型
import time import random from multiprocessing import Process,Queue def consumer(q,name): while True: #消费者会循环接收生产这发出的数据 begin_eat = q.get() if begin_eat == None : break time.sleep(random.random()) #0-1秒内吃一个蛋糕 print("%s吃了%s" % (name,begin_eat)) def producer(q,n): for i in range(n): #生产了10个蛋糕 time.sleep(random.uniform(1,2)) #控制1-2秒生产一个蛋糕 print("生产了蛋糕%s" % i) q.put("蛋糕%s" % i) if __name__ == '__main__': q = Queue() lst = [] for i in range(3): p = Process(target=producer,args=(q,10,)) #在主程序控制生产的数量 p.start() lst.append(p) p2 = Process(target=consumer,args=(q,"alex")) p3 = Process(target=consumer,args=(q,"wusir")) #这里用两个消费者是因为,单个消费者会消费的(吃)的太慢 p2.start() p3.start() for i in lst: i.join() #按照顺序拿出数据 q.put(None) q.put(None) #有几个消费者就要put几个"None"