守护进程、互斥锁、生产者消费者模型

一、守护进程

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}
a.txt

 

三、生产者消费者模型⭐⭐⭐⭐⭐⭐

(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,'-'))
生产者消费者升级版

 

posted @ 2018-07-12 19:22  Smart1san  阅读(186)  评论(0编辑  收藏  举报