在python中,多重处理池中的全局列表变量返回为空?

rfbsl7qr  于 2023-01-14  发布在  Python
关注(0)|答案(4)|浏览(144)

我有一个声明为全局变量的空列表。我给它赋值。但是当我尝试做多处理池时,全局变量是空列表而不是赋值。

from multiprocessing import Process, Pool
camera_data = [{"id": "1", "url": "cam-c.jpg", "area": "1"},
                       {"id": "2", "url": "cam-d.jpg", "area": "1"},
                       {"id": "3", "url": "cam-e.jpg", "area": "2"},
                       {"id": "4", "url": "cam-f.jpg", "area": "2"}]

bulb_data = []

def framed_images(fake):
    print(bulb_data)

if __name__ == '__main__':
    print("camera_data - ",camera_data)
    for data in camera_data:
        bulb_data.append({"url": data["url"], "bulb": False})
    print("bulb_data - ",bulb_data)
    # framed_images()
    fake_Data = bulb_data

    with Pool(processes=4) as pool:
        pool.map(framed_images, fake_Data)

我得到的输出为:

camera_data - [{'id': '1', 'url': 'cam-c.jpg', 'area': '1'}, {'id': '2', 'url': 'cam-d.jpg', 'area': '1'}, {'id': '3', 'url': 'cam-e.jpg', 'area': '2'}, {'id': '4', 'url': 'cam-f.jpg', 'area': '2'}]

bulb_data - [{'url': 'cam-c.jpg', 'bulb': False}, {'url': 'cam-d.jpg', 'bulb': False}, {'url': 'cam-e.jpg', 'bulb': False}, {'url': 'cam-f.jpg', 'bulb': False}]
[]
[]
[]
[]

最后四个空列表来自多处理池。我期望输出如下:

[{'url': 'cam-c.jpg', 'bulb': False}, {'url': 'cam-d.jpg', 'bulb': False}, {'url': 'cam-e.jpg', 'bulb': False}, {'url': 'cam-f.jpg', 'bulb': False}]
[{'url': 'cam-c.jpg', 'bulb': False}, {'url': 'cam-d.jpg', 'bulb': False}, {'url': 'cam-e.jpg', 'bulb': False}, {'url': 'cam-f.jpg', 'bulb': False}]
[{'url': 'cam-c.jpg', 'bulb': False}, {'url': 'cam-d.jpg', 'bulb': False}, {'url': 'cam-e.jpg', 'bulb': False}, {'url': 'cam-f.jpg', 'bulb': False}]
[{'url': 'cam-c.jpg', 'bulb': False}, {'url': 'cam-d.jpg', 'bulb': False}, {'url': 'cam-e.jpg', 'bulb': False}, {'url': 'cam-f.jpg', 'bulb': False}]

以便在全局变量更新时编辑每个进程中的字典列表。

i1icjdpr

i1icjdpr1#

这可能是因为您使用的是spawn启动方法,当使用该方法时,进程只会从产生它们的进程继承bear minimum。这是MacOS和Windows上的默认启动方法,也是Windows操作系统上唯一的启动方法。
您可以在此处阅读有关不同启动方法的文档。
文档还指出了使用spawnforkserver启动方法时全局变量的这一点:
请记住,如果在子进程中运行的代码试图访问全局变量,那么它看到的值(如果有的话)可能与调用Process.start时父进程中的值不同。然而,全局变量只是模块级常量,不会引起任何问题。
https://docs.python.org/3/library/multiprocessing.html?highlight=multiprocessing#the-spawn-and-forkserver-start-methods

niwlg2el

niwlg2el2#

input参数(iterable)是强制性的,并且您希望通过framed_images中未使用的输入和导致该输出的全局列表的技巧来实现这一点。
其背后的原因是,多进程池为该功能创建子进程,并且将与块大小参数相关的自变量(这里是称为fake_Data的列表)划分为每个子进程,并且不存在共享存储器。
你可以像这样引用对象,但是我从来没有试过。https://docs.python.org/3/library/multiprocessing.html#shared-ctypes-objects
虽然,我认为你可以用一个使用共享内存的多线程模块来实现。尝试使用子进程不是你所需要的。

9jyewag0

9jyewag03#

正如所指出的,在Windows(或Mac-OS,因为它们都使用X1 M0 N1 X)上,每个派生的“子”都有自己的单独内存,并导入您的脚本,因此它们基本上是这样做的。

import your_script
print(your_script.bulb_data)

如果你运行这段代码,你会得到一个空列表,在Linux上,它略有不同,你会得到你的“预期结果”,因为它使用fork,但内存仍然没有共享,任何修改其中之一不会影响其他进程。
解决这个问题的方法是使用存在于其他进程中的托管列表,并涉及IPC以跨进程同步该列表。

from multiprocessing import Process, Pool
from multiprocessing import Manager
camera_data = [{"id": "1", "url": "cam-c.jpg", "area": "1"},
                       {"id": "2", "url": "cam-d.jpg", "area": "1"},
                       {"id": "3", "url": "cam-e.jpg", "area": "2"},
                       {"id": "4", "url": "cam-f.jpg", "area": "2"}]


def framed_images(fake):
    print(bulb_data)

def initializer_func(bulb_data_list):
    global bulb_data
    bulb_data = bulb_data_list

if __name__ == '__main__':
    manager = Manager()
    bulb_data = manager.list()
    print("camera_data - ",camera_data)
    for data in camera_data:
        bulb_data.append({"url": data["url"], "bulb": False})
    print("bulb_data - ",bulb_data)
    # framed_images()
    fake_Data = list(bulb_data)

    with Pool(processes=4, initializer=initializer_func, initargs=(bulb_data,)) as pool:
        pool.map(framed_images, fake_Data)

注意,每次访问这个“共享列表”都涉及到IPC,它比普通列表慢,所以要尽量少用它,所以不要在里面放很多对象或大对象。共享状态文档

9o685dep

9o685dep4#

解决方案代码:
使用共享首选项和共享所有子进程的输出与主代码.因此我可以做任何我想要的结果

camera_data = [{"id": "1", "url": "cam-c.jpg", "area": "1"},
                       {"id": "2", "url": "cam-d.jpg", "area": "1"},
                       {"id": "3", "url": "cam-e.jpg", "area": "2"},
                       {"id": "4", "url": "cam-f.jpg", "area": "2"}]

from multiprocessing import SimpleQueue
from multiprocessing.pool import Pool

# initialize worker processes
def init_worker(shared_queue):
    global queue
    queue = shared_queue
    print(queue)

# task executed in a worker process
def task(identifier):
    global queue
    if identifier["area"] == '1':
        queue.put(("GREEN"))
    if identifier["area"] == '2':
        queue.put(("RED"))



# protect the entry point
if __name__ == '__main__':
    # create a shared queue
    shared_queue = SimpleQueue()
    # create and configure the process pool
    fake_data = camera_data
    with Pool(initializer=init_worker, initargs=(shared_queue,)) as pool:
        # issue tasks into the process pool
        _ = pool.map_async(task, fake_data)
        for i in enumerate(fake_data):
            result = shared_queue.get()
            print(f'Got {result}', flush=True)

相关问题