如何防止多处理继承导入和全局?
Posted
技术标签:
【中文标题】如何防止多处理继承导入和全局?【英文标题】:How to prevent multiprocessing from inheriting imports and globals? 【发布时间】:2022-01-12 17:59:50 【问题描述】:我在较大的代码库中使用多处理,其中一些导入语句有副作用。如何在后台进程中运行函数而不让它继承全局导入?
# helper.py:
print('This message should only print once!')
# main.py:
import multiprocessing as mp
import helper # This prints the message.
def worker():
pass # Unfortunately this also prints the message again.
if __name__ == '__main__':
mp.set_start_method('spawn')
process = mp.Process(target=worker)
process.start()
process.join()
背景: 导入 TensorFlow 会初始化 CUDA,这会保留一些 GPU 内存。因此,生成过多进程会导致 CUDA OOM 错误,即使这些进程不使用 TensorFlow。
没有答案的类似问题:
How to avoid double imports with the Python multiprocessing module?【问题讨论】:
您需要在if
语句后面设置导入,或者使用支持fork
的平台作为start_method
即如果multiprocessing.parent_process()
返回None
docs.python.org/3/library/…,您只能导入有问题的模块
@vinzBad 谢谢。我明确设置spawn
来解决一些不安全的导入问题,因为它们启动线程,所以切换回fork
将不起作用,不幸的是。我会在if __name__ == '__main__'
后面关闭进口吗?是否有资源可以准确解释multiprocessing
模块在启动mp.Process
时的作用?这对我的口味来说有点太神奇了:)
如果你在一个单独的文件中定义worker,那么来自父级的导入仍然存在于sys.modules
,尽管它们没有被定义。
@danijar 正如 vinzBad 建议的那样,您可以将导入放在 if __name__ == '__main__':
"guard" 中。启动该过程时,会创建一个新的 Python 解释器并导入相关模块(主模块),然后调用 target
函数(请参阅Examples section 上方的“安全导入主模块”)。因此,if
守卫将在导入模块本身时阻止导入。
【参考方案1】:
# helper.py:
print('This message should only print once!')
# main.py:
import multiprocessing as mp
def worker():
pass
def main():
# Importing the module only locally so that the background
# worker won't import it again.
import helper
mp.set_start_method('spawn')
process = mp.Process(target=worker)
process.start()
process.join()
if __name__ == '__main__':
main()
【讨论】:
这应该会报错,因为本地worker
对象不能被腌制。只需将main
函数的内容直接放在if
保护下即可。
@a_guest 谢谢,已更新。【参考方案2】:
是否有资源可以准确解释多处理 启动
mp.Process
时模块会做什么?
超级快速版本(使用生成上下文而不是分叉)
准备好一些东西(一对用于通信、清理回调等的管道),然后使用fork()
exec()
创建一个新进程。在 Windows 上是 Create
ProcessW()
。使用启动脚本spawn_main()
调用新的python 解释器,并通过crafted command string 和-c
开关传递通信管道文件描述符。启动脚本cleans up the environment 一点点,然后unpickles the Process
object 来自其通信管道。最后是进程对象的calls the run
方法。
那么导入模块呢?
Pickle 语义处理其中一些,但 __main__
和 sys.modules
需要一些 tlc,这是在 here 处理的(在“清理环境”位期间)。
【讨论】:
这非常有帮助,谢谢!想知道为什么我在包文档中找不到太多关于此的内容,尽管许多步骤并不明显。 @danijar 这是所有实现细节,这对用户来说并不重要。理论上只要它完全按照文档中的描述工作,它在引擎盖下的工作方式并不重要.在实践中,我们都知道在实现中总是存在边缘情况(和错误)。以上是关于如何防止多处理继承导入和全局?的主要内容,如果未能解决你的问题,请参考以下文章