Python 多处理 - 独立进程中的全局变量共享 id?



从这个问题中我了解到:

当您使用多处理打开第二个进程时,一个全新的 创建具有自己的全局状态的 Python 实例。那全球 状态不共享,因此子进程对全局进程所做的更改 变量对父进程不可见。

为了验证此行为,我制作了一个测试脚本:

import time
import multiprocessing as mp
from multiprocessing import Pool
x = [0]  # global
def worker(c):
if c == 1:  # wait for proc 2 to finish; is global x overwritten by now?
time.sleep(2)
print('enter: x =', x, 'with id', id(x), 'in proc', mp.current_process())
x[0] = c
print('exit: x =', x, 'with id', id(x), 'in proc', mp.current_process())
return x[0]
pool = Pool(processes=2)
x_vals = pool.map(worker, [1, 2])
print('parent: x =', x, 'with id', id(x), 'in proc', mp.current_process())
print('final output', x_vals)

输出(在CPython上)类似于

enter: x = [0] with id 140138406834504 in proc <ForkProcess(ForkPoolWorker-2, started daemon)>
exit: x = [2] with id 140138406834504 in proc <ForkProcess(ForkPoolWorker-2, started daemon)>
enter: x = [0] with id 140138406834504 in proc <ForkProcess(ForkPoolWorker-1, started daemon)>
exit: x = [1] with id 140138406834504 in proc <ForkProcess(ForkPoolWorker-1, started daemon)>
parent: x = [0] with id 140138406834504 in proc <_MainProcess(MainProcess, started)>
final output [1, 2]

我应该如何解释这样一个事实,即xid在所有过程中都是共享的,但x采用不同的值?从概念上讲id不是 Python 对象的内存地址吗? 我想如果在子进程中克隆内存空间,这是可能的。那么我可以使用什么来获取 Python 对象的实际物理内存地址吗?

共享状态

当您使用多处理打开第二个进程时,将创建一个全新的 Python 实例,该实例具有自己的全局状态。该全局状态不共享,因此子进程对全局变量所做的更改对父进程不可见。

这里的关键点似乎是:

这种全球状态不是共享的...">

。指的是子进程的全局状态。但这并不意味着只要子进程不尝试写入部分,父进程的全局状态部分就不能与子进程共享。发生这种情况时,部分将被复制和更改,并且对父级不可见。

背景:

在Unix上,"fork"是启动子进程的默认方式:

父进程使用 os.fork() 来分叉 Python 解释器。子进程开始时实际上与父进程相同。父进程的所有资源都由子进程继承。请注意,安全分叉多线程进程是有问题的。

仅在 Unix 上可用。Unix 上的默认值。

Fork 是使用写入时复制实现的,因此除非您将新对象分配给x否则不会发生复制,并且子进程与其父进程共享相同的列表。

<小时 />

内存地址

我应该如何解释 x 的 id 在所有进程中共享,但 x 取不同的值这一事实?

Fork 创建一个子进程,其中虚拟地址空间与父进程的虚拟地址空间相同。虚拟地址将全部映射到相同的物理地址,直到发生写入时复制。

现代操作系统使用虚拟寻址。基本上,您在程序中看到的地址值(指针)不是实际的物理内存位置,而是指向索引表(虚拟地址)的指针,而索引表又包含指向实际物理内存位置的指针。由于这种间接寻址,如果虚拟地址属于不同进程的索引表,则可以将相同的虚拟地址指向不同的物理地址。链接


那么我可以使用什么来获取 Python 对象的实际物理内存地址吗?

似乎没有办法获取实际的物理内存地址(链接)。id返回虚拟(逻辑)内存地址 (CPython)。从虚拟内存地址到物理内存地址的实际转换落在 MMU 上。

最新更新