作为Python多处理池参数的对象默认为相同的对象内存(错误)
我试图将对象作为参数传递给一个函数,在这个函数中,我希望使用pythons多处理池并行执行每个函数。但是,每个多重处理函数只调用列表中的最后一个对象 doSomething()中的每个打印对象都使用相同的Temp()对象,并且具有相同的内存地址。输入临时对象都是唯一的,但多处理池似乎只在每个doSomething()函数调用中使用最后一个对象。每个doSomething()都有相同的Temp()对象 为什么会这样?如何将每个对象正确地传递到多处理池中以并行执行作为Python多处理池参数的对象默认为相同的对象内存(错误),python,multiprocessing,Python,Multiprocessing,我试图将对象作为参数传递给一个函数,在这个函数中,我希望使用pythons多处理池并行执行每个函数。但是,每个多重处理函数只调用列表中的最后一个对象 doSomething()中的每个打印对象都使用相同的Temp()对象,并且具有相同的内存地址。输入临时对象都是唯一的,但多处理池似乎只在每个doSomething()函数调用中使用最后一个对象。每个doSomething()都有相同的Temp()对象 为什么会这样?如何将每个对象正确地传递到多处理池中以并行执行 def doSomething(t
def doSomething(temp):
print(temp)
class Temp():
def __init__(self, b):
self.a = 10
self.b = b
def main():
# Create the input args into the multiprocess pool map function
temps_args = ()
bs = [-5, -10, -15]
for b in bs:
temp = Temp(b)
print(temp)
temps_args += (temp,)
# Setup multi-procesing pool and execute multiprocessing cases
pool = multiprocessing.Pool()
res = pool.map(doSomething, temps_args)
if __name__ == '__main__':
main()
创建运行函数的进程时,它将根据父进程的当前状态以自己的内存启动。在多进程处理中,单个进程可能被多次重复使用以减少启动时间。在3条记录的情况下,很可能它们都达到了相同的过程 继续之前: 当一个Python对象被垃圾收集时,该对象的id将返回循环。以此为例:
class Foo:
pass
# All instances of Foo are kept in memory before ids are fetched
ids_for_alive = set(id(foo) for foo in [Foo() for _ in range(1000)])
print(len(ids_for_alive))
# prints 1000
# After every call to id, the last Foo instance reference is dropped and may be gc'd
ids_for_gc = set(id(Foo()) for _ in range(1000))
print(len(ids_for_gc))
# usually prints 1
class Foo:
def __init__(self, val):
self.val = val
def get_id(foo):
print(foo.val)
return id(foo)
if __name__ == '__main__':
pool = multiprocessing.Pool()
foos = [Foo(i) for i in range(3)]
ids = set(pool.map(get_id, foos))
print(f"Total ids: {len(ids)}")
对我来说,当对象保存在内存中时,这只是打印1000
,而当对象没有引用时,这只是打印1
。他们被允许收集垃圾,这意味着id一直可用,单个id被重复使用1000次
返回到多处理:
当工作进程被指派一个作业并收到其参数时,它们将被加载到内存中并分配一个id。当该作业结束时,对该参数的引用将被删除,这意味着它可以被垃圾回收,并再次可用。例如:
class Foo:
pass
# All instances of Foo are kept in memory before ids are fetched
ids_for_alive = set(id(foo) for foo in [Foo() for _ in range(1000)])
print(len(ids_for_alive))
# prints 1000
# After every call to id, the last Foo instance reference is dropped and may be gc'd
ids_for_gc = set(id(Foo()) for _ in range(1000))
print(len(ids_for_gc))
# usually prints 1
class Foo:
def __init__(self, val):
self.val = val
def get_id(foo):
print(foo.val)
return id(foo)
if __name__ == '__main__':
pool = multiprocessing.Pool()
foos = [Foo(i) for i in range(3)]
ids = set(pool.map(get_id, foos))
print(f"Total ids: {len(ids)}")
对我来说,这是印刷的:
0
1
2
Total ids: 1
现在,您可以做一件有趣的事情(一点也不高效)就是限制单个进程可以执行的作业数量。如果将此值设置为1,则每次调用任务都会得到一个新进程,很可能还会得到一个新地址空间和一组id值
if __name__ == '__main__':
pool = multiprocessing.Pool(maxtasksperchild=1)
foos = [Foo(i) for i in range(3)]
ids = set(pool.map(get_id, foos))
print(f"Total ids: {len(ids)}")
印刷品:
0
1
2
Total ids: 3
希望这有助于澄清问题 它们实际上不是同一个对象-请记住,
doSomething
的每个实例都在一个单独的进程中运行,具有完全独立的内存空间。由于所有的内存空间开始时都是彼此的克隆,因此从主进程接收的args对象在每个进程中都位于相同的地址也就不足为奇了。如果您在函数中也执行了print(temp.b)
,我很确定您会看到它们接收到不同的对象,即使它们的字符串表示是相同的。为什么您认为它们是相同的对象?您是对的,它正在为这些temp对象打印不同的内容。对于对象更复杂的实际问题(树,充满节点),多处理函数调用在每个函数调用中使用相同的对象,而输入都是唯一的对象。任何关于为什么会出现这种情况的提示/指针?添加一个\uuuuu str\uuuuuuuuu
或\uuuuuu repr\uuuuuuu
到Temp
中,您将看到一切正常。