队列
进程彼此之间本身是相互隔离的,如果要实现进程间通信,multiprocessing模块支持两种形式:队列和管道
我们一般都以队列的方式去实现我们进程之间的通讯
Queue(maxsize)#可以在初始化的时候设置最大能存几条数据
q.put#存放数据的方式,每调用一次就可以存放一次
#两个参数
1.blocked#(阻塞) 配合timeout等使用,一般默认为True
2.timeout#若blocked为true,你设置该参数后,在一定时间内若队列为满,导致你的put()中的数无法加入队列,会在到了你规定的时间爆出异常
q.get#取数据的方式,从队列中取出一个数据并删除
q.empty()#若队列为空时返回True,但是不可靠,可能在返回值的时候有数据加入
q.full()#若队列满返回True,不可靠
q.qsize()#返回队列中目前的正确数量,不可靠
生产者消费者模型
在并发编程中使用生产者和消费者模式能够解决大多数的并发问题。该模式通过平衡生产线程与消费者线程的工作能力来提高程序的整体处理数据的速度。
简单的说,就是我们若需要实现一个功能,假设他有一个功能是产出数据的,另一个则是依赖产出的数据进行数据的运算,这种时候我们就可以使用这个模型。
但是我们为了提高效率,这种时候我们就可以依赖于进程+队列去实现本效果。
from multiprocessing import Process, Queue
import time
import random
def produce(q):
for i in range(3):
time.sleep(random.randint(0,3))#模拟制作的时间
res = '厨师生产了包子%s' % i
print(res)
q.put(f'包子{i}')
def consume(q):
while True:
res = q.get()
time.sleep(random.randint(0,3))
print('顾客买了%s'%res)
if __name__ == '__main__':
q = Queue()
p = Process(target=produce, args=(q,))
c = Process(target=consume, args=(q,))
p.start()
c.start()
print('主')
上述方法就是生产者消费者模型的使用,但是光是这样,上述代码也是有一定的缺陷的。
joinableQuene与守护者进程的搭配使用
joinableQuene与Queue最大的区别就是多了join方法与task_done方法
在之前的代码中我们发现,尽管消费者已经将队列所有的东西取完,但是程序并没有结束,我们的程序卡在了消费者的q.get()方法上面。现在我们需要解决的问题是,找一个合适的时候让我们的程序退出
这里有两种方法
一:在生产者生产完毕后往队列中添加特定的值,消费者取出该值时退出循环
from multiprocessing import Process, Queue
import time
import random
def produce(q):
for i in range(3):
time.sleep(random.randint(0,3))
res = '厨师生产了包子%s' % i
print(res)
q.put(f'包子{i}')
def consume(q):
while True:
res = q.get()
if res == None: break
time.sleep(random.randint(0,3))
print('顾客买了%s'%res)
if __name__ == '__main__':
q = Queue()
p = Process(target=produce, args=(q,))
c = Process(target=consume, args=(q,))
p.start()
c.start()
p.join()
q.put(None)
print('主')
二:使用joinableQuene与守护者进程(推荐使用)
from multiprocessing import Process, JoinableQueue
import time
import random
def produce(q):
for i in range(3):
time.sleep(random.randint(0,3))
res = '厨师生产了包子%s' % i
print(res)
q.put(f'包子{i}')
def consume(q):
while True:
res = q.get()
if res == None: break
time.sleep(random.randint(0,3))
print('顾客买了%s'%res)
q.task_done()
if __name__ == '__main__':
q = JoinableQueue()
p = Process(target=produce, args=(q,))
c = Process(target=consume, args=(q,))
p.daemon = True
c.daemon = True
p.start()
c.start()
#保证生产者生产完,不然若队列中的数据为0就会跳过q的阻塞
p.join()
q.join()
print('主')
上述两种方式都可以实现功能,我个人觉得最大的区别就是主进程的结束,方式一主进程会在生产者结束后就可能结束,而二方式则是保证主进程结束后在结束两者的子进程,但还是推荐方式二