准确确定在Python多处理过程中酸洗的内容
问题描述
如线程What is being pickled when I call multiprocessing.Process?中所述,在某些情况下,多处理几乎不需要通过酸洗传输数据。例如,在Unix系统上,解释器使用fork()
创建进程,并且每个进程都可以访问在多进程启动时已经存在的对象,而不需要进行筛选。
然而,我正在尝试考虑更多的场景,这里是它应该如何工作的&q;例如,代码可能有错误,并且应该为只读的对象被无意中修改,从而导致其酸洗被转移到其他进程。
有没有某种方法可以确定在多处理期间哪些内容或至少有多少内容是酸洗的?信息不一定必须是实时的,但如果有一种方法可以获得一些统计数据(例如,被酸洗的对象数量),这将是很有帮助的,它可能会提示为什么由于意外的酸洗开销,某些东西的运行时间比预期的要长。
我正在寻找一种在Python环境内部的解决方案。进程跟踪(例如,Linuxstrace
)、网络监听、通用IPC统计信息以及可能用于统计进程之间移动的字节数的类似解决方案不够具体,无法识别对象选取与其他类型的通信。
更新:令人失望的是,除了修改模块和/或解释器源代码之外,似乎无法收集酸洗统计数据。然而,@Aaron确实解释了这一点,并澄清了几个小问题,所以我接受了答案。
解决方案
多重处理不完全是一个简单的库,但一旦您熟悉了它的工作原理,就很容易找到它了。
您通常希望从context.py开始。这是所有有用的类根据操作系统进行绑定的地方,并且...嗯..。您的上下文&Q;已激活。有4个基本上下文:用于POSIX的Fork、ForkServer和Spron;以及用于Windows的单独派生。它们又各自拥有自己的&popen";(在start()
处调用)来启动一个新进程来处理单独的实现。
popen_fork.py
创建进程的字面意思是调用os.fork()
,然后在子进程中组织运行BaseProcess._bootstrap()
,这会设置一些清理内容,然后调用self.run()
来执行您给它的代码。不会以这种方式启动进程,因为整个内存空间都会被复制(除了某些例外。参见:fork(2))。
open_spawn_xxxx.py
我最熟悉Windows,但我认为Win32和POSIX版本的操作方式非常相似。使用一个简单的精心制作的命令行字符串创建一个新的python进程,该字符串包括一对用于读/写的管道句柄。新流程将导入__main__模块(通常等于sys.argv[0]
),以便访问所有需要的引用。然后,它将执行一个简单的引导函数(从命令字符串),该函数尝试从创建对象的管道中读取并取消选取Process
对象。一旦它有了Process
实例(一个新对象,它是副本,而不仅仅是对原始对象的引用),它将再次安排调用_bootstrap()
。
popen_forkserver.py
第一次使用";forkserver";上下文创建新流程时,新流程将运行一个处理新流程请求的简单服务器(侦听管道)。后续流程请求都转到同一服务器(基于导入机制和服务器实例的模块级别全局)。然后,从该服务器派生新的进程,以便节省启动新的Python实例的时间。然而,这些新进程不能有任何相同的(如在同一对象中而不是副本中)Process
对象,因为派生它们的Python进程本身就是&spwn";ed。因此,Process
实例与";spawn";非常相似。这种方法的好处包括:执行派生的进程是单线程的,以避免死锁。开发一个新的Python解释器的成本只需支付一次。解释器和__main__导入的任何模块的内存消耗可以很大程度上共享,因为分叉和写入时复制通常使用内存页。
在所有情况下,一旦发生拆分,您应该认为内存空间是完全独立的,它们之间的唯一通信是通过管道或共享内存。锁和信号量由扩展库(用c编写)处理,但基本上都是由操作系统管理的命名信号量。、
Pipe
和multiprocessing.Manager
的、Pipe
和multiprocessing.Manager
使用Pickle将更改同步到它们返回的代理对象。new-ishmultiprocessing.shared_memory
使用内存映射文件或缓冲区共享数据(由操作系统像信号量一样管理)。
解决您的问题:
这只适用于代码可能有错误,应为只读的对象被意外修改,导致其酸洗被转移到其他进程。
multiprocessing.Manager
代理对象。因为其他所有操作都要求您对发送和接收数据非常谨慎,或者改用除酸洗以外的其他传输机制。
相关文章