1.守护进程

"守护进程的特点"
1.守护进程会在主进程的代码执行完成后终止
2.守护进程内不能再开启子进程,否则抛出异常AssertionError: daemonic processes are not allowed to have children
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
import time
import random

def task(name):
    print('%s is piaoing' %name)
    time.sleep(random.randrange(1,3))
    print('%s is piao end' %name)

if __name__ == '__main__':
    p=Process(target=task,args=('egon',))
    p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行
    p.start()
    print('主') #只要终端打印出这一行内容,那么守护进程p也就跟着结束掉了

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
主

Process finished with exit code 0
from multiprocessing import Process

import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")

if __name__ == '__main__':
    p1=Process(target=foo)
    p2=Process(target=bar)

    p1.daemon=True
    p1.start()
    p2.start()
    print("main-------") #主进程执行玩这个,p1就停止了

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
main-------
456
end456

Process finished with exit code 0

2.互斥锁

2.1互斥锁案例

"如果我希望下面的代码输出如下效果
进程0 is running
进程0 is done
进程1 is running
进程1 is done
进程2 is running
进程2 is done
"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

from multiprocessing import Process
import time
def work(info):
    print('%s is running'%info)
    time.sleep(2)
    print('%s is done'%info)

if __name__ == '__main__':
    for i in range(3):
        p=Process(target=work,args=("进程%s"%i,))
        p.start()

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
进程0 is running
进程1 is running
进程2 is running
进程0 is done
进程1 is done
进程2 is done

Process finished with exit code 0
"通过多进程,输出的效果与预期不同,那怎样才能实现那样的效果呢?除了join,还可以使用互斥锁"
"互斥锁把并发改成了串行,降低了效率,但是保证了数据有序和安全"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

from multiprocessing import Process,Lock
import time
def work(info,lock):
    lock.acquire() #加锁,只能一个进程处理
    print('%s is running'%info)
    time.sleep(2)
    print('%s is done'%info)
    lock.release() #释放锁,别的进程可以进行处理了

if __name__ == '__main__':
    lock = Lock()
    for i in range(3):
        p=Process(target=work,args=("进程%s"%i,lock,))
        p.start()

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
进程0 is running
进程0 is done
进程1 is running
进程1 is done
进程2 is running
进程2 is done

Process finished with exit code 0
"达到了预期效果"

2.2互斥锁-模拟抢票

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
import time,json

def search(name):
    dic=json.load(open('db.txt'))
    time.sleep(1)
    print('\033[43m%s 查到剩余票数%s\033[0m' %(name,dic['count']))

def get(name):
    dic=json.load(open('db.txt'))
    time.sleep(1) #模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(1) #模拟写数据的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[46m%s 购票成功\033[0m' %name)

def task(name):
    search(name)
    get(name)

if __name__ == '__main__':
    for i in range(10): #模拟并发10个客户端抢票
        name='<路人%s>' %i
        p=Process(target=task,args=(name,))
        p.start()
"如下图所示,所有人都购票成功了,是有问题的。"

多进程续集_第1张图片

"加互斥锁的购票"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
#把文件db.txt的内容重置为:{"count":1}
from multiprocessing import Process,Lock
import time,json

def search(name):
    dic=json.load(open('db.txt'))
    time.sleep(1)
    print('\033[43m%s 查到剩余票数%s\033[0m' %(name,dic['count']))

def get(name):
    dic=json.load(open('db.txt'))
    time.sleep(1) #模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(1) #模拟写数据的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[46m%s 购票成功\033[0m' %name)
    else:
        print('\033[46m%s 余票不足\033[0m' %name)

def task(name,lock):
    search(name)
    with lock: #相当于lock.acquire(),执行完自代码块自动执行lock.release()
        get(name)

if __name__ == '__main__':
    lock=Lock()
    for i in range(10): #模拟并发10个客户端抢票
        name='<路人%s>' %i
        p=Process(target=task,args=(name,lock))
        p.start()

多进程续集_第2张图片

2.3互斥锁与join

使用join也可以把并发变成串行,互斥锁的原理也是把并发变成串行,那是不是直接使用join()就可以了,请看下面的演示
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
#把文件db.txt的内容重置为:{"count":1}
from multiprocessing import Process,Lock
import time,json

def search(name):
    dic=json.load(open('db.txt'))
    print('\033[43m%s 查到剩余票数%s\033[0m' %(name,dic['count']))

def get(name):
    dic=json.load(open('db.txt'))
    time.sleep(1) #模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(1) #模拟写数据的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[46m%s 购票成功\033[0m' %name)

def task(name,):
    search(name)
    get(name)

if __name__ == '__main__':
    for i in range(10):
        name='<路人%s>' %i
        p=Process(target=task,args=(name,))
        p.start()
        p.join()

"我们看到,连查票也变成串行的效果了,我们想要查票是并行的查询,购票是串行的,就只能使用互斥锁的方式了"

多进程续集_第3张图片

"总结
互斥锁可以让一个进程中某一段代码或某一个函数编程串行运行
join()是让一个进程的整体变为串行运行"

3.队列(Queue)

加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务进行修改,即串行操作,虽然牺牲了速度,但获得了数据的安全
虽然可以使用文件共享数据实现进程间的数据通信,但问题是:
1.效率低(数据是基于文件的,需要到磁盘上获取数据)
2.需要自己加锁
因此,需要找到一个方法兼顾
1.效率高
2.不需要处理锁的问题
"这就是multiprocessing模块提供的基于消息的IPC通信机制:队列和管道"
队列和管道都是把数据存在内存中
队列是基于(管道+锁)实现的,可以让我们不需要处理锁的问题,因而队列称为进程间通信的最佳选择。

3.1队列介绍

"创建队列的类"
Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
"参数介绍"
maxsize是队列中允许最大数据数
注意:
1.队列中存放的是消息,不是大数据
2.队列占用的是内存空间,因为maxsize即使没有大小限制,也会受到内存大小的限制。
"主要方法介绍"
q.put()把数据放入到队列中
q.get()从队列中获取数据,并把数据从队列中删除
"队列的使用"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process,Queue

q=Queue(3)

#put ,get ,put_nowait,get_nowait,full,empty
q.put(1)
q.put(2)
q.put(3)
print(q.full()) #满了
# q.put(4) #再放就阻塞住了,卡住不动了

print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #空了
# print(q.get()) #再取就阻塞住了,卡住不动了

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
True
1
2
3
True

Process finished with exit code 0

3.2队列-生产者消费者模型

3.2.1生产者消费者模型介绍

"为什么要使用生产者消费者模型"
生产者指的是生产数据的任务,消费者指的是处理数据的任务
在并发编程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。
同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。
为了解决这个问题于是引入了生产者和消费者模式。

"什么是生产者和消费者模式"
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。
生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯
所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列
消费者不找生产者要数据,而是直接从阻塞队列里取
阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

这个阻塞队列就是用来给生产者和消费者解耦的

3.2.2生产者消费者模型实现

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process,Queue
import time,random,os
def consumer(q,name):
    while True:
        res=q.get()
        time.sleep(random.randint(1,3))
        print('\033[43m%s 吃 %s\033[0m' %(name,res))

def producer(q,name,food):
    for i in range(3):
        time.sleep(random.randint(1,3))
        res='%s%s' %(food,i)
        q.put(res)
        print('\033[45m%s 生产了 %s\033[0m' %(name,res))

if __name__ == '__main__':
    q=Queue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=(q,'vita','包子'))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,'lili'))

    #开始
    p1.start()
    c1.start()
    print('主')

"此时的问题是如下图,程序永远不会结束,因为生产者生产完成后就结束了,消费者卡在了get()这一步,一直等待消息"

多进程续集_第4张图片

"上面问题的解决方法:
可以让生产者发送一个结束信号,消费者接收到了,就break"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process,Queue
import time,random,os
def consumer(q,name):
    while True:
        res=q.get()
        if res is None:
           break
        time.sleep(random.randint(1,3))
        print('\033[43m%s 吃 %s\033[0m' %(name,res))

def producer(q,name,food):
    for i in range(3):
        time.sleep(random.randint(1,3))
        res='%s%s' %(food,i)
        q.put(res)
        print('\033[45m%s 生产了 %s\033[0m' %(name,res))

if __name__ == '__main__':
    q=Queue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=(q,'vita','包子'))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,'lili'))

    #开始
    p1.start()
    c1.start()
    #需要等上面的执行完成了
    p1.join()
    q.put(None)
    print('主')

多进程续集_第5张图片

"上面的问题是有几个消费者,就需要put()几个结束信号"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process,Queue
import time,random,os
def consumer(q,name):
    while True:
        res=q.get()
        if res is None:
           break
        time.sleep(random.randint(1,3))
        print('\033[43m%s 吃 %s\033[0m' %(name,res))

def producer(q,name,food):
    for i in range(3):
        time.sleep(random.randint(1,3))
        res='%s%s' %(food,i)
        q.put(res)
        print('\033[45m%s 生产了 %s\033[0m' %(name,res))

if __name__ == '__main__':
    q=Queue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=(q,'vita','包子'))
    p2 = Process(target=producer, args=(q, 'vita1', '包子'))
    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,'lili'))
    c2 = Process(target=consumer, args=(q, '超超'))
    #开始
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    #需要等上面的执行完成了,
    # 注意,这里只需要生产者join()就可以了,谁join(),主进程就会等谁工作完成
    # 所以我们会看到主进程代码完成后,还有消费者在消费
    p1.join()
    p2.join()
    q.put(None)
    q.put(None)
    print('主')

多进程续集_第6张图片

3.2.3JoinableQueue([maxsize])-生产者消费者模型

"上面我们需要发送多个结束信号,那我们能不能不发送结束信号呢"
"参数介绍"
maxsize是队列中允许最大数据数,省略则是没有大小限制
"方法介绍"
q.task_done():使用者使用此方法发出信号,表示q.get()的返回值已经被处理。
如果调用此方法的次数大于从队列中删除项目的数量,将返回ValueError异常。
q.join():生产者调用此方法进行阻塞,知道队列中所有项目均被处理。
阻塞将持续到队列中的每个项目均调用q.task_done()为止
"JoinableQueue()的生产者消费者模型"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process,JoinableQueue
import time,random,os
def consumer(q,name):
    while True:
        res=q.get()
        time.sleep(random.randint(1,3))
        print('\033[43m%s 吃 %s\033[0m' %(name,res))
        q.task_done() #发送信号给q.join(),说明已经从队列中取走一个数据并处理完毕了

def producer(q,name,food):
    for i in range(3):
        time.sleep(random.randint(1,3))
        res='%s%s' %(food,i)
        q.put(res)
        print('\033[45m%s 生产了 %s\033[0m' %(name,res))
    q.join() #等到消费者把自己放入队列中的所有的数据都取走之后,生产者才结束

if __name__ == '__main__':
    q=JoinableQueue() #使用JoinableQueue()

    #生产者们:即厨师们
    p1=Process(target=producer,args=(q,'vita1','苹果'))
    p2=Process(target=producer,args=(q,'vita2','香蕉'))
    p3=Process(target=producer,args=(q,'vita3','橘子'))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,'chaochao1'))
    c2=Process(target=consumer,args=(q,'chaochao2'))
    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('主')

多进程续集_第7张图片

3.2.4生产者消费者模型总结

1.程序中有两类角色
    生产者-生产数据
    消费者-取出数据
2.引入该模型的原因
    平衡生产者与消费者之间的速度差
    程序解耦合
3.如何实现生产者消费者模型
 生产者--放-》队列《-取--消费者