sj9*_*126 6 python pickle multiprocessing
正如线程中所解释的,当我调用 multiprocessing.Process? 时正在腌制什么?在某些情况下,多处理几乎不需要通过 pickling 传输数据。例如,在 Unix 系统上,解释器用于fork()创建进程,并且每个进程可以访问多处理启动时已存在的对象,而无需进行 pickling。
然而,我正在尝试考虑“这就是它应该如何工作”之外的场景。例如,代码可能存在错误,并且本应只读的对象被无意中修改,导致其pickle被转移到其他进程。
有没有某种方法可以确定在多处理过程中腌制了什么或至少腌制了多少?这些信息不一定必须是实时的,但如果有一种方法可以获得一些统计数据(例如,腌制的对象数量),这可能会提示为什么某些东西需要更长的时间来运行,这将很有帮助由于意外的酸洗开销,超出了预期。
我正在寻找 Python 环境内部的解决方案。进程跟踪(例如Linux strace)、网络窥探、广义IPC 统计以及可用于计算进程之间移动的字节数的类似解决方案都不够具体,无法识别对象酸洗与其他类型的通信。
更新:令人失望的是,除了破解模块和/或解释器源之外,似乎没有办法收集酸洗统计数据。然而,@aaron 确实解释了这一点并澄清了一些小问题,所以我接受了答案。
多重处理并不完全是一个简单的库,但是一旦您熟悉了它的工作原理,就很容易探索并弄清楚它。
您通常希望从context.py开始。这是所有有用的类根据操作系统进行绑定的地方,并且......好吧......您所活跃的“上下文”。有 4 个基本上下文:Fork、ForkServer 和 Spawn for posix;以及一个单独的 Windows Spawn。这些依次都有自己的“Popen”(称为start())来启动一个新进程来处理单独的实现。
创建一个进程实际上会调用os.fork(),然后在子进程中组织运行,BaseProcess._bootstrap()设置一些清理内容,然后调用self.run()来执行您提供的代码。以这种方式启动进程不会发生任何酸洗,因为整个内存空间都会被复制(有一些例外。请参阅: fork(2))。
我最熟悉 Windows,但我认为 win32 和 posix 版本的操作方式非常相似。使用简单的命令行字符串创建一个新的 python 进程,其中包括一对用于读取/写入的管道句柄。新进程将导入 __main__ 模块(通常等于sys.argv[0]),以便访问所有需要的引用。然后它将执行一个简单的引导函数(来自命令字符串),尝试从创建对象的管道中读取和取消pickleProcess对象。一旦它拥有Process实例(一个新对象,它是一个副本;而不仅仅是对原始对象的引用),它将再次安排调用_bootstrap().
第一次使用“forkserver”上下文创建新进程时,将“生成”一个新进程,运行一个处理新进程请求的简单服务器(侦听管道)。后续流程请求都转到同一服务器(基于导入机制和服务器实例的模块级全局)。然后从该服务器“分叉”出新进程,以节省启动新 python 实例的时间。然而,这些新进程不能有任何相同的Process对象(如在同一个对象中,而不是副本),因为它们派生的 python 进程本身是“生成”的。因此,实例Process被腌制并发送,就像“spawn”一样。此方法的优点包括: 进行分叉的进程是单线程的,以避免死锁。启动一个新的 python 解释器的成本只需支付一次。由于“fork”通常使用写时复制内存页,因此解释器和 __main__ 导入的任何模块的内存消耗在很大程度上可以共享。
在所有情况下,一旦发生分割,您应该考虑完全独立的内存空间,并且它们之间的唯一通信是通过管道或共享内存。锁和信号量由扩展库(用 c 编写)处理,但基本上被命名为由操作系统管理的信号量。Queue's、Pipe's 和multiprocessing.Manager's 使用 pickling来同步它们返回的代理对象的更改。new-ishmultiprocessing.shared_memory使用内存映射文件或缓冲区来共享数据(由操作系统管理,如信号量)。
该代码可能存在错误,并且无意中修改了本应只读的对象,导致其酸洗转移到其他进程。
这仅适用于multiprocessing.Manager代理对象。因为其他一切都要求您非常有意识地发送和接收数据,或者使用除酸洗之外的其他传输机制。