目录
为什么你的多进程池卡住了
你正在使用 multiprocessing
来在多进程中运行一些代码,但它却卡住了。它一动不动。
你检查了 CPU 使用率——没有任何动静,它没有做任何工作。
到底发生了什么?
在许多情况下,你可以通过一行代码来修复这个问题——跳到文章末尾试试看——但首先,让我们深入探讨 Python 的缺陷和 POSIX 系统编程的痛苦,使用一些令人兴奋但不太有说服力的鲨鱼隐喻!
让我们设定一个隐喻场景:你正在一个充满鲨鱼的游泳池里游泳。(鲨鱼是进程的隐喻。)
接下来,你拿起一把叉子。(叉子是 fork()
的隐喻。)
你用叉子刺自己。刺啊刺啊。血流了出来,鲨鱼开始围着你转,很快你就会发现自己——死锁在水中!
在这次时空之旅中,你将遇到:
- 一个神秘的故障,Python 的
multiprocessing.Pool
莫名其妙地死锁了。 - 这个谜题的根源:
fork()
。 - 一个难题:
fork()
复制所有东西是一个问题,而fork()
不复制所有东西也是一个问题。 - 一些无法止血的创可贴。
- 一个能防止你的代码被鲨鱼吃掉的解决方案。
让我们开始吧!
介绍 multiprocessing.Pool
Python 提供了一个方便的模块,允许你在进程池中运行任务,这是提高程序并行性的好方法。(注意,这些示例没有在 Windows 上测试;我在这里专注于 *nix 平台。)
from multiprocessing import Pool
from os import getpid
def double(i):
print("I'm process", getpid())
return i * 2
if __name__ == '__main__':
with Pool() as pool:
result = pool.map(double, [1, 2, 3, 4, 5])
print(result)
如果我们运行这个代码,会得到:
I'm process 4942
I'm process 4943
I'm process 4944
I'm process 4942
I'm process 4943
[2, 4, 6, 8, 10]
如你所见,double()
函数在不同的进程中运行。
一些应该可以工作但实际上不行的代码
不幸的是,虽然 Pool
类很有用,但它也充满了凶猛的鲨鱼,随时等着你犯错。例如,以下完全合理的代码:
import logging
from threading import Thread
from queue import Queue
from logging.handlers import QueueListener, QueueHandler
from multiprocessing import Pool
def setup_logging