多进程二
守护进程:
主进程创建守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def foo(n): 2 print('foo') 3 time.sleep(2) 4 print('foo .. end') 5 6 def bar(m): 7 print('bar') 8 time.sleep(5) 9 print('bar .. end') 10 11 if __name__ == '__main__': 12 p=Process(target=foo,args=(2,)) 13 p1=Process(target=bar,args=(3,)) 14 p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行 15 p.start() 16 p1.start() 17 print('主')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#主进程代码运行完毕,守护进程就会结束 from multiprocessing import Process from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") p1=Process(target=foo) p2=Process(target=bar) p1.daemon=True p1.start() p2.start() print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from multiprocessing import Process 2 import os,time,random 3 4 def task(): 5 print('%s is running' %os.getpid()) 6 time.sleep(random.randint(1,3)) 7 print('%s is end' %os.getpid()) 8 9 if __name__ == '__main__': 10 p1=Process(target=task) 11 p2=Process(target=task) 12 p3=Process(target=task) 13 p1.start() 14 p1.join() 15 p2.start() 16 p2.join() 17 p3.start() 18 p3.join() 19 print('主')
进程同步锁/互斥锁
进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,
竞争带来的结果就是错乱,如何控制,就是加锁处理
示列一:多个进程共享同一个打印终端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process import os,time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) if __name__ == '__main__': for i in range(3): p=Process(target=work) p.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process,Lock import os,time def work(lock): lock.acquire() print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) lock.release() if __name__ == '__main__': lock=Lock() for i in range(3): p=Process(target=work,args=(lock,)) p.start()
示例二:多个进程共享一个文件,文件好比数据库,模拟抢票
#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
Lock:锁
acquire:加锁
release:解锁
with:自动加上锁,待代码执行完后自动解开锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process,Lock import time,json,random def search(): dic=json.load(open('db.txt')) print('\033[43m剩余票数%s\033[0m' %dic['count']) def get(): dic=json.load(open('db.txt')) time.sleep(0.1) #模拟读数据的网络延迟 if dic['count'] >0: dic['count']-=1 time.sleep(0.2) #模拟写数据的网络延迟 json.dump(dic,open('db.txt','w')) print('\033[43m购票成功\033[0m') def task(lock): search() get() if __name__ == '__main__': lock=Lock() for i in range(100): #模拟并发100个客户端抢票 p=Process(target=task,args=(lock,)) p.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import json import time import random from multiprocessing import Process,Lock def search(n): dic=json.load(open("db.txt")) time.sleep(1) print('\033[33m <%s> 查到剩余票数 [%s]\033[0m'%(n,dic['count'])) def get(n): dic=json.load(open('db.txt')) if dic['count'] > 0: dic['count']-=1 time.sleep(1) # 模拟网络延迟 json.dump(dic,open('db.txt','w')) print('\033[34m <%s> 购票成功\033[0m'%n) else: # print('<%s>购买失败'%n) print('\033[31m <%s> 购票失败\033[0m'%n) def task(n,lock): search(n) # lock.acquire() # get(n) # lock.release() with lock: get(n) if __name__ == '__main__': lock=Lock() for i in range(5): p=Process(target=task,args=(i,lock)) p.start()
总结:
#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。 虽然可以用文件共享数据实现进程间通信,但问题是: 1.效率低(共享数据基于文件,而文件是硬盘上的数据) 2.需要自己加锁处理 #因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。 1 队列和管道都是将数据存放于内存中 2 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来, 我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。
生产者消费者模型:
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很 慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个解耦的过程。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Queue,Process import time,random def producer(name,q): for i in range(10): time.sleep(random.randint(1,3)) res='泔水%s'%i q.put(res) print('厨师 %s 生产了 %s' %(name,res)) def consumer(name,q): while True: res=q.get() if res is None:break time.sleep(random.randint(1,3)) print('%s 吃了 %s' %(name,res)) if __name__ == '__main__': q=Queue() p1=Process(target=producer,args=('egon',q)) c1=Process(target=consumer,args=('alex',q)) p1.start() c1.start() p1.join() q.put(None)
IPC机制:
IPC机制就是进程之间的相互通信,分别有两种方式:一种是队列(管道+锁),另一种是管道
队列的原理是:先进先出,只能是一头传入数据,另一头读取数据。管道+锁就能实现队列。
管道的原理是:
队列和管道实现了多个进程之间的共享内存空间。
Queue:指定队列中存放的数据个数。
put:写入数据
get:读取数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Queue q=Queue(3) q.put({"name":"egon"}) q.put("123456") q.put((1111)) # q.put_nowait(1111111) print(q.get()) print(q.get()) print(q.get()) # q.get_nowait(1111111)