如何防止多处理继承导入和全局?

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 上是 CreateProcessW()。使用启动脚本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 这是所有实现细节,这对用户来说并不重要。理论上只要它完全按照文档中的描述工作,它在引擎盖下的工作方式并不重要.在实践中,我们都知道在实现中总是存在边缘情况(和错误)。

以上是关于如何防止多处理继承导入和全局?的主要内容,如果未能解决你的问题,请参考以下文章

使用全局axios拦截器时如何防止出现多次401错误警告

如何从python中的方法执行全局from foo import bar?

打字稿:如何在全局范围内进行一些导入?

java多线程,如何防止脏读数据

python中的多处理模块和修改共享的全局变量

父进程全局变量如何复制到python多处理中的子进程