守护进程、互斥锁、生产者消费者模型
一、守护进程
1.定义:
守护进程是守护父进程的子进程,所以守护进程实际上也是子进程的一类,守护进程会伴随着父进程的消亡而消亡。
2.为什么有守护进程?
当我们需要实现并发的目的的时候,就需要开启多个子进程。这些子进程中的某些子进程虽然将代码执行完毕,但是这类子进程们没有结束的运行条件,所以,这类子进程中的某些进程并不一定会随着父进程的毁灭而毁灭,但是这类进程又没有存在的意义了,这时候就需要用到守护进程。
3.如何使用守护进程?
1 from multiprocessing import Process 2 import os,time 3 def Foo(): 4 print('%s is eating..'%os.getpid()) 5 time.sleep(3) 6 print('%s was eat over..'%os.getpid()) 7 8 def Bar(): 9 print('%s is going to school'%os.getpid()) 10 time.sleep(3) 11 print('%s classes over'%os.getpid()) 12 13 if __name__ == '__main__': 14 p1=Process(target=Foo) 15 p2=Process(target=Bar) 16 p1.daemon=True # 这个代表p1是守护进程,而且必须写在子进程 17 # 向操作系统发起开启子进程信号之前 18 p1.start() 19 # p1.daemon=True # AssertionError: process has already started 20 p2.start() 21 print("I'm main process")
二、互斥锁
1.定义:
在涉及到共享数据的部分,需要将涉及到该部分的代码加上一个锁,使之变成串行(一个程序执行完毕,才会执行下一个程序)。
2.为什么要有互斥锁?
现如今的时代是个数据大爆发的时代,这就避免不了需要涉及到数据的共享,但这随之带来了数据安全性的问题。就拿平时网购这个问题来举例,假设一件商品的库存只剩下1件,这个时候多个客户端同时向服务端发送请求,申请查看该件商品的库存数据,服务端同时给各个客户端回应之后,在这之前都基本上没有什么大问题;但是在这之后,假如多个客户端同时又向服务端发送了一个购买的请求,在没有互斥锁的情况下,各个客户端都可以正常下单购买,但是我们的商品库存就只剩1件了!!!所以这种情况下,根本是不合理的。在有互斥锁的情况下,当各个客户端发送购买请求的时候,可以将这些请求按照请求的顺序排列,一个请求进入之后,其余的请求均只能在外面等待该请求的完成操作(不管购买成功或者下单失败)。这就是互斥锁存在的意义,当然前面的例子是在忽略网络延迟的前提下进行的。
3.互斥锁使用例子
3.1 多个客户端同时网购查看库存和下单购买示例
1 from multiprocessing import Process,Lock 2 import time,os,json,random 3 # time.sleep() 是模拟网络延迟 4 5 def look(): 6 time.sleep(random.randint(0,1)) 7 with open('a.txt','r',encoding='utf8') as f: 8 dic=json.load(f) 9 print('%s查到商品的库存是<%s>'%(os.getpid(),dic['count'])) 10 11 def buy(): 12 time.sleep(random.randint(0,2)) 13 with open('a.txt','rt',encoding='utf8') as f: 14 dic=json.load(f) 15 if dic['count']>0: 16 dic['count']-=1 17 with open('a.txt','wt',encoding='utf8') as f: 18 json.dump(dic,f) 19 print('%s 成功购买了该商品'%os.getpid()) 20 else: 21 print('%s 商品库存不足,下单失败'%os.getpid()) 22 def get(mutex): 23 look() 24 with mutex: # 给buy加上一个锁,同一时间只能有一个客户端进入 25 buy() 26 if __name__ == '__main__': 27 mutex=Lock() 28 for i in range(3): # 用循环来模拟多个客户端 29 p=Process(target=get,args=(mutex,)) 30 p.start()
1 {"count":1}
三、生产者消费者模型⭐⭐⭐⭐⭐⭐
(1)了解生产者消费者模型之前,我们先来了解下 IPC 机制
IPC 机制:Inter-Process Communication,进程间通信。IPC机制遵循的就是先进先出的原则
IPC占用的是内存的空间,不应该往队列里边放大数据
IPC 有两种实现方式:
1.管道(PIPE) :一端只能放进去东西,而另一端只能从管道中取东西。
2.队列(管道+锁) :在管道的前提下,为其添加上了一个锁,使之能够有顺序排列
(2)示例
from multiprocessing import Queue q=Queue(3) # 将队列的空间设成3,默认是无限大, # 但是现实中碍于内存大小,它实际上还是有限制的 q.put('cake') q.put('banana') q.put('cookies') # q.put('apple') # 队列满了之后不会在往队列里边添加任何东西了 print(q.get()) print(q.get()) print(q.get())
1.生产者消费者定义
生产者:比喻的是程序运行中负责产生数据的任务
消费者:比喻的是程序运行中负责处理数据的任务
2.为何使用该模型?
可以实现生产者与消费者之间的解耦和,生产者在不停生产的同时,消费者也可以不停的消费;这种情况下才能最大化的提高程序的运行效率,平衡了生产者的生产能力和消费者的消费能力。
3.怎么使用?
当我们的程序中明显的需要用到两个程序,一个程序不断地产生数据,而另一个程序不断地负责处理数据的时候,就需要用到该模型。
1 from multiprocessing import Queue,Process 2 import time,os,random 3 4 def producer(name,food,q): 5 for i in range(3): 6 res='%s %s'%(food,i) 7 time.sleep(random.randint(0,2)) 8 q.put(res) 9 print('\033[46m%s 生产了 %s\033[0m'%(name,res)) 10 # q.put(None) # q.put(None) 写在这里会造成消费者有可能还未销毁完, 11 # 然后这个None已经由生产者发送到消费者手中了,程序就运行结束了 12 def consumer(name,q): 13 while True: 14 res=q.get() 15 if res is None:break 16 time.sleep(random.randint(1,3)) 17 print('%s 销毁了 %s '%(name,res)) 18 19 20 21 if __name__ == '__main__': 22 q=Queue() 23 # 生产者们 24 p1=Process(target=producer,args=('大胖','火箭',q)) 25 p2=Process(target=producer,args=('二胖','坦克',q)) 26 p3=Process(target=producer,args=('三胖','原子弹',q)) 27 28 # 消费者们 29 c1=Process(target=consumer,args=('小三',q)) 30 c2=Process(target=consumer,args=('小四',q)) 31 32 p1.start() 33 p2.start() 34 p3.start() 35 36 c1.start() 37 c2.start() 38 39 p1.join() 40 p2.join() 41 p3.join() 42 43 # 生产者生产完毕之后才应该放出结束信号 44 q.put(None) # q.put(None) 写在这里的意思是生产者都已经生产完毕,我们可以发 45 # 送结束信号给消费者, 消费者收到信号之后,就会结束运行,不会一直等 46 # 待接收生产者的产品了 47 q.put(None) # 若只写了一个q.put(None),则只有一个消费者会收到生产结束的信 48 # 号,另一个消费者会继续等待接收,所以要写两个 49 print('supervisor')
1 from multiprocessing import Process,JoinableQueue 2 import time,random 3 4 def producer(name,product,q): 5 for i in range(3): 6 res='%s %s'%(product,i) 7 time.sleep(random.randint(0,2)) 8 print('\033[46m %s produce %s \033[m'%(name,res)) 9 q.put(res) 10 11 def consumer(name,q): 12 while True: 13 res=q.get() 14 time.sleep(random.randint(0,2)) 15 print('%s eat %s'%(name,res)) 16 q.task_done() # 代表队列里边的值已经被取光了, 17 # 取光了就说明生产者已经全部生产完了, 18 # 并且消费者也都消费完了 19 20 21 if __name__ == '__main__': 22 q=JoinableQueue() 23 p1=Process(target=producer,args=('A','cake',q)) 24 p2=Process(target=producer,args=('B','sun',q)) 25 p3=Process(target=producer,args=('C','moon',q)) 26 27 c1=Process(target=consumer,args=('GG',q)) 28 c2=Process(target=consumer,args=('MM',q)) 29 c1.daemon=True 30 c2.daemon=True # 主进程结束之后消费者的进程也会随之结束 31 # 消费者再吃完之后,虽然任务已经完成,但是消费者还是会继续等待接收, 32 # 为了结束程序,我们可以将消费者设置成守护进程,伴随主进程的消亡而消亡 33 34 35 p1.start() 36 p2.start() 37 p3.start() 38 39 c1.start() 40 c2.start() 41 42 p1.join() 43 p2.join() 44 p3.join() 45 46 q.join() # 这里的意思是管道队列里的东西取完之后,才会运行父进程的代码(前提是下面还有代码) 47 48 print('main process'.center(20,'-'))