本篇博客主要介绍Python中使用了不恰当的进程间通信方式导致进程阻塞的解决方案。
最近,帮本科生辅导竞赛,遇到了一个问题,项目中用到了多进程,涉及到多进程,自然少不了进程间通信。
大概场景为:一个进程需要将数据传给另一个进程。最开始使用的进程间通信方式为Queue,初始容量为10个元素。但是当时问题是,生产数据的进程运行速度比消费数据的进程快,导致Queue很快被填满,导致生产数据的进程阻塞。但是这里生产数据的进程还有综合显示的功能,导致显示效果一顿一顿的,效果非常差。
我们来简单模拟一下上述问题:
from multiprocessing import Process, Queue
import time
# 生产者
def producer(q_production):
num = 1
while True:
print("q_production")
q_production.put(num)
num = num + 1
# 消费者
def consumer(q_consume):
while True:
try:
num = q_consume.get()
except:
num = 0
pass
print("q_consume: %d" % num)
# 手动延时1s,让消费者比生产者慢
time.sleep(1)
if __name__ == "__main__":
# 容量为10
q = Queue(10)
test1 = Process(target = producer, args = (q, ))
test1.start()
test2 = Process(target = consumer, args = (q, ))
test2.start()
我们来看一下效果:
可以看到效果为生产者刚开始迅速将队列填满,填满之后生产者进程就阻塞了,只有在队列中有空位置的时候才可以向其中添加数据。
有什么解决方法呢?
方法一:
我们可以使用Queue的put_nowait()
和get_nowait()
,这两个方法都是非阻塞的,这样就不会导致生产者进程阻塞了。注意:put_nowait()
在队列满的时候会抛异常。
from multiprocessing import Process, Queue
import time
def producer(q_production):
num = 1
while True:
print("q_production")
try:
# 非阻塞将数据放入队列
# 注意:队列满时会抛异常
q_production.put_nowait(num)
except:
pass
num = num + 1
def consumer(q_consume):
while True:
try:
# 非阻塞获取队列中的数据
num = q_consume.get_nowait()
except:
num = 0
pass
print("q_consume: %d" % num)
time.sleep(1)
if __name__ == "__main__":
q = Queue(10)
test1 = Process(target = producer, args = (q, ))
test1.start()
test2 = Process(target = consumer, args = (q, ))
test2.start()
另外,这样虽然解决了生产者进程阻塞的问题,但是项目中需要消费者进程获取到的数据是实时的,也就是最新的,Queue无法解决这个问题。
方法二:
改用共享内存的进程间通信方式:
开辟一个数据大小的共享内存,生产者进程不停的去更新这个数据,消费者进程每次去读取这个数据得到的都是最新的值。
from multiprocessing import Process, Value
import time
def producer(memWrite):
num = 1
while True:
print("memWrite!")
# 不停的更新共享内存中的值
memWrite.value = num
num = num + 1
def consumer(memRead):
while True:
# 读取共享内存中的内容
print("memRead: %d" % memRead.value)
time.sleep(1)
if __name__ == "__main__":
# 申请一块共享内存,大小为2个字节,初始值为0
shareMem = Value('i', 0)
test1 = Process(target = producer, args = (shareMem, ))
test1.start()
test2 = Process(target = consumer, args = (shareMem, ))
test2.start()