python multiprocessing模块的坑

本文探讨了Python多进程环境下Queue的使用误区,包括进程join导致的死锁现象及Queue.empty()方法的不可靠性。阐述了如何通过哨兵值或先验知识判断生产者-消费者模型中队列是否真正为空,避免程序异常终止。

1.进程join造成死锁

当代码如下,进程正常结束:

from multiprocessing import Process, Queue

def f(q):
    q.put('X' * 60000)

if __name__ == '__main__':
    queue = Queue()
    p1 = Process(target=f, args=(queue,))
    p1.start()
    p1.join()                    # this deadlocks
    obj = queue.get()

但将f函数中的60000改成70000时,程序无法终止:

from multiprocessing import Process, Queue

def f(q):
    q.put('X' * 70000)

if __name__ == '__main__':
    queue = Queue()
    p1 = Process(target=f, args=(queue,))
    p1.start()
    p1.join()                    # this deadlocks
    obj = queue.get()

在这里插入图片描述
根据官方文档的说明,当一个进程被join时,Python会检查被放入Queue中的数据是否已经全部删除,若没有删除,则进程会一直处于等待状态。在第一份代码中,程序虽然被join了,但是有可能当Queue的size小于某个阈值时,进程join的时候会将其视为空Queue,所以可以正常结束。

2.Queue.empty()不可信

当代码如下,有时程序可以结束,而有时不可以。

from multiprocessing import Process, Queue
import os

def proc_read(q):
    print('Process(%s) is reading...' % os.getpid())
    cnt = 0
    while True:
        cnt += 1
        url = q.get(True)
        print("### final: {}".format(cnt))

if __name__=='__main__':
    q = Queue()

    for i in range(10000):
        q.put(1)

    proc_reader = Process(target=proc_read, args=(q,))
    proc_reader.start()

    while not q.empty() :
        pass

    print("The size of Queue is {}".format(q.qsize()))
    # proc_reader进程里是死循环,无法等待其结束,只能强行终止:
    proc_reader.terminate()
    print("The process can't finish.")

结果:
在这里插入图片描述
原因是Queue.empty()是不可信的,可能会返回错误的结果。如果在生产者-消费者问题中要用到Queue,让消费者知道什么时候队列为空有两种方式:

  • 使用哨兵,当生产者进程put完之后,再向队列中put一个None,当消费者进程读到None之后,则知道自己的工作已经完成了(如果是多生产者和多消费者,每个生产者结束都应该put一个None,消费者检测到和生产者个数相同的None即可得知任务结束,同时消费者get到一个None之后应该把它再放回队列)。
  • 使用先验知识,先计算出生产者总共会生产多少数据,然后消费者处理完对应数量的数据即可得知任务结束。

不过仍然不清楚为什么这个case中,empty会好端端地在队列中有数据的情况下返回了一个True,以及整个进程为什么不能终止,以后有时间读源码再仔细了解一下这个机制。






参考资料:
https://docs.python.org/zh-cn/3.6/library/multiprocessing.html
https://stackoverflow.com/questions/32395150/how-to-make-pythons-multiprocessing-queues-empty-method-return-the-correct?noredirect=1&lq=1

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值