作为Python多处理池参数的对象默认为相同的对象内存(错误)

作为Python多处理池参数的对象默认为相同的对象内存(错误),python,multiprocessing,Python,Multiprocessing,我试图将对象作为参数传递给一个函数,在这个函数中,我希望使用pythons多处理池并行执行每个函数。但是,每个多重处理函数只调用列表中的最后一个对象 doSomething()中的每个打印对象都使用相同的Temp()对象,并且具有相同的内存地址。输入临时对象都是唯一的,但多处理池似乎只在每个doSomething()函数调用中使用最后一个对象。每个doSomething()都有相同的Temp()对象 为什么会这样?如何将每个对象正确地传递到多处理池中以并行执行 def doSomething(t

我试图将对象作为参数传递给一个函数,在这个函数中,我希望使用pythons多处理池并行执行每个函数。但是,每个多重处理函数只调用列表中的最后一个对象

doSomething()中的每个打印对象都使用相同的Temp()对象,并且具有相同的内存地址。输入临时对象都是唯一的,但多处理池似乎只在每个doSomething()函数调用中使用最后一个对象。每个doSomething()都有相同的Temp()对象

为什么会这样?如何将每个对象正确地传递到多处理池中以并行执行

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
中,您将看到一切正常。