目标:多个生产者(放入队列数据), 和一个监督者(输入字符,可输出指定信息, 如队列长度,生产总个数等等)
先看看最初的代码(单生产者):
import time
from multiprocessing import Process, Queue
cache = Queue(maxsize=100)
# 生产区
def Product():
t1 = 1
while True:
time.sleep(0.5)
cache.put(f'苹果{t1}')
t1 += 1
# 启动生产进程
P1 = Process(target=Product)
P1.daemon = True
P1.start()
# 监督区
print('(监督区) 请输入相关操作指令:')
while True:
try:
ComTO = int(input())
if ComTO == 1:
print(P1.is_alive())
print(f'当前生产总个数:{cache.qsize()}')
except Exception as e:
print(f'发生错误:{e}')
第一坑:进程一定要放到main中:
代码测试结果:
解决方式:进程的使用放入main中
...
if __name__ == '__main__':
# 启动生产线程
P1 = Process(target=Product)
P1.daemon = True
P1.start()
# 监督区
print('(监督区) 请输入相关操作指令:')
while True:
try:
ComTO = int(input())
if ComTO == 1:
print(P1.is_alive())
print(f'当前缓存个数:{cache.qsize()}')
except Exception as e:
print(f'发生错误:{e}')
然后就进入了第二坑
第二坑 队列在进程中,一定要用传参的方式调用队列(直接使用全局变量不行,不知道底层原因。。。)
以上最终代码测试结果(输入时都是有延迟的,没有狂按输入):
分析和解决方式
可以看到进程P1是正在运行中的,按道理应该队列中是有值了,但是实际输出时,队列一直为空。很明显队列中没有数据,后来经过测试,发现队列需要传进去,直接使用全局变量是不行的。
# 生产区
def Product(Cache):
print('开始生产')
t1 = 1
while True:
time.sleep(0.5)
Cache.put(f'苹果{t1}')
t1 += 1
# 启动生产线程
P1 = Process(target=Product, args=(cache, ))
P1.daemon = True
P1.start()
测试结果:
细心点的肯定都发现了另外一个小问题,刚开始运行时,都是在我输入第一个值以后才开始生产,但理论上应该是互不影响。
(自己理解的,不知是不是真的原因,修改后确实可用)
这里是因为进程还未启动完成,就遇到了input,这个时候就要等待input完成后再接下来输出,然后在第二个input之前就启动了进程,这个时候加个小延迟就好了。
...
P1 = Process(target=Product, args=(cache, ))
P1.daemon = True
P1.start()
time.sleep(0.1)
# 监督区
print('(监督区) 请输入相关操作指令:')
...
然后在进程中添加几个线程,分别负责生产。
# 生产区
def Product(Cache):
print('开始生产')
def Apple(Cache1):
t2 = 1
while True:
time.sleep(0.5)
Cache1.put(f'苹果{t2}')
t2 += 1
def Banana(Cache1):
t2 = 1
while True:
time.sleep(0.5)
Cache1.put(f'香蕉{t2}')
t2 += 1
T1 = Thread(target=Apple, args=(Cache,))
T2 = Thread(target=Banana, args=(Cache,))
T1.daemon = True
T2.daemon = True
T1.start()
T2.start()
第三坑 多进程中有线程(守护线程)时,一定要让进程保持存活,不然线程也会挂掉
以上最终代码测试结果:
分析和解决方式
可以看到进程在第一次输入时,就已经挂掉了。
原因是因为进程中有且只有线程,线程一启用,进程中就已经执行完了,所以进程结束,同样该进程的守护线程也挂掉了。
这个解决方法就比较多了,要么加个延迟(time.sleep()), 要么去掉一个线程直接放到进程中。这两个方法都有一定缺点,最好还是加个循环判断:
def Product(Cache):
...
T1.start()
T2.start()
# 如果有一个线程存活就等待一秒
while T1.is_alive() or T2.is_alive():
time.sleep(1)
return
...
最终代码(原有基础上增加一个全输出功能):
import time
from multiprocessing import Process, Queue
from threading import Thread
cache = Queue(maxsize=100)
# 生产区
def Product(Cache):
print('开始生产')
def Apple(Cache1):
t2 = 1
while True:
time.sleep(0.5)
Cache1.put(f'苹果{t2}')
t2 += 1
def Banana(Cache1):
t2 = 1
while True:
time.sleep(0.5)
Cache1.put(f'香蕉{t2}')
t2 += 1
T1 = Thread(target=Apple, args=(Cache,))
T2 = Thread(target=Banana, args=(Cache,))
T1.daemon = True
T2.daemon = True
T1.start()
T2.start()
# 如果有一个线程存活就等待一秒
while T1.is_alive() or T2.is_alive():
time.sleep(1)
return
def Check():
print('(监督区) 请输入相关操作指令:')
while True:
try:
ComTO = int(input())
if ComTO == 1:
print(P1.is_alive())
print(f'当前生产总个数:{cache.qsize()}')
elif ComTO == 2:
temp = []
while cache.qsize() != 0:
temp.append(cache.get())
print(f'输出全部:{temp}')
except Exception as e:
print(f'发生错误:{e}')
if __name__ == '__main__':
# 启动生产
P1 = Process(target=Product, args=(cache,))
P1.daemon = True
P1.start()
time.sleep(0.1)
# 启动监督
Check()
结果:
完美!