互斥锁与队列进程数据共享
一、互斥锁:
那一个抢票的例子来说:一个文件中放一行数据为票的数量 {"number": 1 } 票数为1
from multiprocessing import Process,Lock import json import time def set(i): data_read = json.load(open('info_user.txt', 'r', encoding='utf-8')) # 先查看票数,所有人都可以一起查看 time.sleep(2) # 模拟读数据的网络延迟 print('%s 查看有%s张票' % (i,data_read['number'])) def take(i): data = json.load(open('info_user.txt', 'r', encoding='utf-8')) if data['number']>0: data['number'] = data['number']-1 time.sleep(2) # 模拟写数据的网络延迟 json.dump(data,open('info_user.txt','w',encoding='utf-8')) print('%s 购票成功 ' % i) def run(i): set(i) take(i) if __name__ == '__main__': for i in range(10): p = Process(target=run,args=(i,)) p.start()
结果:会发现所有的人都买到票了,但是票只有一张,多进程效率虽然高但是数据混乱不安全,所以这个互斥锁就是解决数据混乱不安全这个问题的
有1张票 有1张票 有1张票 有1张票 有1张票 有1张票 有1张票 有1张票 有1张票 有1张票 0 购票成功 1 购票成功 2 购票成功 3 购票成功 4 购票成功 5 购票成功 6 购票成功 7 购票成功 8 购票成功 9 购票成功
解决: 这里加上了互斥锁,舍弃效率保证了数据的安全性
from multiprocessing import Process,Lock import json import time def set(i): data_read = json.load(open('info_user.txt', 'r', encoding='utf-8')) # 先查看票数,所有人都可以一起查看 time.sleep(2) # 模拟读数据的网络延迟 print('%s 查看有%s张票' % (i,data_read['number'])) def take(i): data = json.load(open('info_user.txt', 'r', encoding='utf-8')) if data['number']>0: data['number'] = data['number']-1 time.sleep(2) # 模拟写数据的网络延迟 json.dump(data,open('info_user.txt','w',encoding='utf-8')) print('%s 购票成功 ' % i) def run(i,lock): set(i) lock.acquire() # 这里也可以用with lock: 相当于lock.acquire(),执行完自代码块自动执行lock.release() take(i) lock.release() if __name__ == '__main__': lock = Lock() for i in range(10): p = Process(target=run,args=(i,lock)) p.start()
结果:
0 查看有1张票 2 查看有1张票 3 查看有1张票 1 查看有1张票 4 查看有1张票 6 查看有1张票 5 查看有1张票 8 查看有1张票 7 查看有1张票 9 查看有1张票 0 购票成功
二、队列与管道
队列与管道:队列和管道都是将数据存放于内存中,而队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,因而队列才是进程间通信的最佳选择
obj = Queue(max_size) 定义方式,max_size是限制队列中的个数
但是:队列中的占用的是内存空间,max_size是受内存大小限制的
队列中放的数据不能是大型的数据
# q.put方法用以插入数据到队列中。 # q.get方法可以从队列读取并且删除一个元素。 from multiprocessing import Queue q = Queue(3) q.put('1234') q.put([1,2,3,'e']) q.put(1) print(q.full()) # 查看该队列是否已经满了 print(q.get()) print(q.get()) print(q.get()) print(q.empty()) # 是否已经为空 # print(q.get()) # 拿完后队列里没有值了,就死在这里了,阻塞了
三、队列中的生产者与消费者模型
生产者与消费者模型我是这样理解的,生产者只顾生产,消费者只顾消费,两个是分开进行的,绝不是生产者生产一个消费者就消费一个,生产者还要等消费者消费完再生产一个
1、队列中的堵塞
from multiprocessing import Process,Queue import time def producer(q): for i in range(10): time.sleep(0.5) ret = '包子 %s' % i print('生产者生产了 %s' % ret ) q.put(ret) # 生产一个就往队列里面放一个 def consumer(q): while 1: ret = q.get() # 当队列中的值取完后再取就会进行阻塞,一直阻塞到有人往里面放值了,代码再往下走 if ret==None:break time.sleep(1) print('消费者吃了 %s' % ret) if __name__ == '__main__': q = Queue() p1 = Process(target=producer,args=(q,)) c1 = Process(target=consumer,args=(q,)) p1.start() c1.start() print('主')
队列阻塞解决:在生产者生产结束的时候给消费者发送一个消息,说我生产完了没有了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process,Queue import time def producer(q): for i in range(10): time.sleep(0.5) ret = '包子 %s' % i print('生产者生产了 %s' % ret ) q.put(ret) # 生产一个就往队列里面放一个 def consumer(q): while 1: ret = q.get() # 取一个出来 if ret==None:break time.sleep(1) print('消费者吃了 %s' % ret) if __name__ == '__main__': q = Queue() p1 = Process(target=producer,args=(q,)) c1 = Process(target=consumer,args=(q,)) p1.start() c1.start() p1.join() # 在确保生产者进程结束了 q.put(None) # 发送None,但是如果有多个生产者那每个都要进行发送,感觉很low,还有一种队列解决方法 print('主')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process,JoinableQueue import time def producer(q): for i in range(10): time.sleep(0.5) ret = '包子 %s' % i print('生产者生产了 %s' % ret ) q.put(ret) q.join() # 生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止 def consumer(q): while 1: ret = q.get() time.sleep(1) print('消费者吃了 %s' % ret) q.task_done() # q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常 if __name__ == '__main__': q = JoinableQueue() p1 = Process(target=producer,args=(q,)) p2 = Process(target=producer,args=(q,)) p3 = Process(target=producer,args=(q,)) c1 = Process(target=consumer,args=(q,)) c2 = Process(target=consumer,args=(q,)) c1.daemon=True c2.daemon=True p1.start() p2.start() p3.start() c1.start() c2.start() p1.join() p2.join() p3.join() # 1、主进程等生产者p1、p2、p3结束 # 2、而p1、p2、p3是在消费者把所有数据都取干净之后才会结束 # 3、所以一旦p1、p2、p3结束了,证明消费者也没必要存在了,应该随着主进程一块死掉,因而需要将生产者们设置成守护进程 print('主')