首页 > 解决方案 > 如何正确引用 Multiprocessing Pool.map 中的类的实例?

问题描述

假设我定义了以下类:

class Animal:
    def __init__(self):
        self.isAlive = True

连同以下功能:

def Kill_Animal(animal):
    animal.isAlive = False

现在,如果我创建一个动物列表,如下所示:

AnimalsList = [Animal() for i in range(0,5)]

如果该函数应用于列表中的动物类的任何实例,则 isAlive 属性将更改为 False。但是,如果我想将此函数应用于此列表并通过多处理库更改其内容,那么正确的方法是什么?

我尝试了以下方法:

from multiprocessing import Process, Pool

pool = Pool()
pool.map(Kill_Animal, AnimalsList[0:3])

但是,如果我尝试检查列表中所有元素的属性,结果如下:

[print(animal.isAlive) for animal in AnimalsList]

输出: 真 真 真 真 真

此外,如果我尝试检查在运行时通过 Pool.Map 传递给 Kill_Animal 函数的对象的 ID,它与对象自己的 ID 不匹配。我熟悉 Python 的 call-by-object 引用,但这里发生了什么?

标签: pythonpython-3.xpython-multiprocessing

解决方案


在研究了多处理文档之后,我理解了对这个概念的误解。

使用multiprocessing,即使类的实例作为参数传递,ID 与调用方法中的 ID 不同也是有意义的,因为现在我们完全在不同的Process中工作,因此这个对象是一个副本原始对象的位置,并且不对应于内存中的相同位置。因此,副本中所做的任何更改都不会影响其原始实例。

为了使用并行和共享状态,必须应用一个不同的概念,即多线程,如基于线程的并行文档中提供的那样。在这里彻底讨论了多线程和多处理之间的区别:Multiprocessing vs Threading Python

回到最初的问题,可以实现两种简单的方法来遍历 List 并应用函数:

1. 使用multiprocessing.dummy

multiprocessing.dummy 复制了多处理的 API,但只不过是线程模块的包装器。

所以答案可以写成:

import multiprocessing.dummy as mp
p = mp.Pool(3) # With 3 being the number of threads.
p.map(Kill_Animal, AnimalsList)
p.close()
p.join()

[print(animal.isAlive) for animal in AnimalsList]

输出: 假 假 假 假 假

2.使用队列

from queue import Queue
from threading import Thread

# Creates the hunter thread.
def hunter():
    while True:
        animal = q.get()
        Kill_Animal(animal)
        q.task_done()

num_hunter_threads = 3
q = Queue()

#Initialize the threads
for i in range(num_hunter_threads):
    t = Thread(target=hunter)
    t.daemon = True
    t.start()

#Adds each animal in the list to the Queue.
for animal in AnimalsList:
    q.put(animal)

#Execute the jobs in the queue.
q.join()

[print(animal.isAlive) for animal in AnimalsList)

输出: 假 假 假 假 假


推荐阅读