为什么 Python Multiprocessing Worker 不会死?
Why won't Python Multiprocessing Workers die?
我正在使用 python 多处理功能将某些功能映射到某些元素。类似这样的事情:
def computeStuff(arguments, globalData, concurrent=True):
pool = multiprocessing.Pool(initializer=initWorker, initargs=(globalData,))
results = pool.map(workerFunction, list(enumerate(arguments)))
return results
def initWorker(globalData):
workerFunction.globalData = globalData
def workerFunction((index, argument)):
... # computation here
通常我 运行 在 ipython 中使用 cPython 和 Pypy 进行测试。我注意到生成的进程通常不会被杀死,所以它们开始累积,每个进程都使用一个 ram。在计算过程中按 ctrl-k 时会发生这种情况,这会使多处理陷入混乱的狂潮。但即使让计算完成,这些进程也不会在 Pypy 中消亡。
根据文档,当池被垃圾回收时,它应该调用 terminate()
并终止所有进程。这里发生了什么事?我必须明确调用 close()
吗?如果是,是否有某种上下文管理器可以正确管理关闭资源(即进程)?
这是在 Mac OS X Yosemite.
PyPy 的垃圾收集是惰性的,所以调用 close
失败意味着 Pool
被清理 "sometime",但这可能并不意味着 "anytime soon".
一旦 Pool
正确 close
d,工作人员在 运行 完成任务时退出。确保 Pool
在 pre-3.3 Python 中关闭的简单方法是:
from contextlib import closing
def computeStuff(arguments, globalData, concurrent=True):
with closing(multiprocessing.Pool(initializer=initWorker, initargs=(globalData,))) as pool:
return pool.map(workerFunction, enumerate(arguments))
注意:我还删除了到 list
的显式转换(毫无意义,因为 map
将为您迭代 enumerate
迭代器),并 returned 结果直接(无需仅在下一行为 return 分配名称)。
如果你想确保在异常情况下立即终止(在 pre-3.3 Python),你可以使用 try/finally 块,或者编写一个简单的上下文管理器(可以是重复用于您使用 Pool
) 的其他地方):
from contextlib import contextmanager
@contextmanager
def terminating(obj):
try:
yield obj
finally:
obj.terminate()
def computeStuff(arguments, globalData, concurrent=True):
with terminating(multiprocessing.Pool(initializer=initWorker, initargs=(globalData,))) as pool:
return pool.map(workerFunction, enumerate(arguments))
terminating
方法的优越之处在于它保证进程立即退出;从理论上讲,如果您在主程序的其他地方使用线程,Pool
worker 可能会与非守护线程分叉,即使 worker 任务线程退出,这也会使进程保持活动状态; terminating
通过强行终止进程来隐藏它。
如果您的解释器是 Python 3.3 或更高版本,terminating
方法内置于 Pool
,因此 with
语句不需要特殊包装器, with multiprocessing.Pool(initializer=initWorker, initargs=(globalData,)) as pool:
直接工作
我正在使用 python 多处理功能将某些功能映射到某些元素。类似这样的事情:
def computeStuff(arguments, globalData, concurrent=True):
pool = multiprocessing.Pool(initializer=initWorker, initargs=(globalData,))
results = pool.map(workerFunction, list(enumerate(arguments)))
return results
def initWorker(globalData):
workerFunction.globalData = globalData
def workerFunction((index, argument)):
... # computation here
通常我 运行 在 ipython 中使用 cPython 和 Pypy 进行测试。我注意到生成的进程通常不会被杀死,所以它们开始累积,每个进程都使用一个 ram。在计算过程中按 ctrl-k 时会发生这种情况,这会使多处理陷入混乱的狂潮。但即使让计算完成,这些进程也不会在 Pypy 中消亡。
根据文档,当池被垃圾回收时,它应该调用 terminate()
并终止所有进程。这里发生了什么事?我必须明确调用 close()
吗?如果是,是否有某种上下文管理器可以正确管理关闭资源(即进程)?
这是在 Mac OS X Yosemite.
PyPy 的垃圾收集是惰性的,所以调用 close
失败意味着 Pool
被清理 "sometime",但这可能并不意味着 "anytime soon".
一旦 Pool
正确 close
d,工作人员在 运行 完成任务时退出。确保 Pool
在 pre-3.3 Python 中关闭的简单方法是:
from contextlib import closing
def computeStuff(arguments, globalData, concurrent=True):
with closing(multiprocessing.Pool(initializer=initWorker, initargs=(globalData,))) as pool:
return pool.map(workerFunction, enumerate(arguments))
注意:我还删除了到 list
的显式转换(毫无意义,因为 map
将为您迭代 enumerate
迭代器),并 returned 结果直接(无需仅在下一行为 return 分配名称)。
如果你想确保在异常情况下立即终止(在 pre-3.3 Python),你可以使用 try/finally 块,或者编写一个简单的上下文管理器(可以是重复用于您使用 Pool
) 的其他地方):
from contextlib import contextmanager
@contextmanager
def terminating(obj):
try:
yield obj
finally:
obj.terminate()
def computeStuff(arguments, globalData, concurrent=True):
with terminating(multiprocessing.Pool(initializer=initWorker, initargs=(globalData,))) as pool:
return pool.map(workerFunction, enumerate(arguments))
terminating
方法的优越之处在于它保证进程立即退出;从理论上讲,如果您在主程序的其他地方使用线程,Pool
worker 可能会与非守护线程分叉,即使 worker 任务线程退出,这也会使进程保持活动状态; terminating
通过强行终止进程来隐藏它。
如果您的解释器是 Python 3.3 或更高版本,terminating
方法内置于 Pool
,因此 with
语句不需要特殊包装器, with multiprocessing.Pool(initializer=initWorker, initargs=(globalData,)) as pool:
直接工作