Python 使用多处理将来自多个数据加载程序的对象填充到队列中

Python 使用多处理将来自多个数据加载程序的对象填充到队列中,python,multiprocessing,generator,Python,Multiprocessing,Generator,我在机器学习输入管道上工作。我编写了一个数据加载器,它从一个大的.hdf文件中读取数据并返回切片,每个切片大约需要2秒。因此,我想使用一个队列,它接收来自多个数据加载器的对象,并可以通过下一个函数(如生成器)从队列返回单个对象。此外,填充队列的进程应该在后台运行,在队列未满时重新填充队列。我不能让它正常工作。它与单个数据加载器一起工作,给了我4倍的相同片段 import multiprocessing as mp class Queue_Generator(): def __init_

我在机器学习输入管道上工作。我编写了一个数据加载器,它从一个大的.hdf文件中读取数据并返回切片,每个切片大约需要2秒。因此,我想使用一个队列,它接收来自多个数据加载器的对象,并可以通过下一个函数(如生成器)从队列返回单个对象。此外,填充队列的进程应该在后台运行,在队列未满时重新填充队列。我不能让它正常工作。它与单个数据加载器一起工作,给了我4倍的相同片段

import multiprocessing as mp

class Queue_Generator():
    def __init__(self, data_loader_list):
        self.pool = mp.Pool(4)
        self.data_loader_list = data_loader_list
        self.queue = mp.Queue(maxsize=16)
        self.pool.map(self.fill_queue, self.data_loader_list)
    def fill_queue(self,gen):
        self.queue.put(next(gen))
    def __next__(self):
        yield self.queue.get()
我从中得到的是: NotImplementedError:池对象不能在进程之间传递或pickle
提前感谢

您的特定错误意味着您在将类方法传递给池时,不能将池作为类的一部分。我的建议如下:

import multiprocessing as mp
from queue import Empty


class QueueGenerator(object):
    def __init__(self, data_loader_list):
        self.data_loader_list = data_loader_list
        self.queue = mp.Queue(maxsize=16)

    def __iter__(self):
        processes = list()
        for _ in range(4):
            pr = mp.Process(target=fill_queue, args=(self.queue, self.data_loader_list))
            pr.start()
            processes.append(pr)
        return self

    def __next__(self):
        try:
            return self.queue.get(timeout=1) # this should have a value, otherwise your loop will never stop. make it something that ensures your processes have enough time to update the queue but not too long that your program freezes for an extended period of time after all information is processed
        except Empty:
            raise StopIteration

# have fill queue as a separate function
def fill_queue(queue, gen):
    while True:
        try:
            value = next(gen)
            queue.put(value)
        except StopIteration: # assumes the given data_loader_list is an iterator
            break
    print('stopping')


gen = iter(range(70))

qg = QueueGenerator(gen)


for val in qg:
    print(val)
# test if it works several times:
for val in qg:
    print(val)

我认为您要解决的下一个问题是让数据加载器列表在每个单独的过程中提供新的信息。但既然你没有提供任何相关信息,我也帮不了你。但是,上面确实提供了一种让进程填充队列的方法,然后将队列作为迭代器传递出去

您的特定错误意味着您在向池传递类方法时不能将池作为类的一部分。我的建议如下:

import multiprocessing as mp
from queue import Empty


class QueueGenerator(object):
    def __init__(self, data_loader_list):
        self.data_loader_list = data_loader_list
        self.queue = mp.Queue(maxsize=16)

    def __iter__(self):
        processes = list()
        for _ in range(4):
            pr = mp.Process(target=fill_queue, args=(self.queue, self.data_loader_list))
            pr.start()
            processes.append(pr)
        return self

    def __next__(self):
        try:
            return self.queue.get(timeout=1) # this should have a value, otherwise your loop will never stop. make it something that ensures your processes have enough time to update the queue but not too long that your program freezes for an extended period of time after all information is processed
        except Empty:
            raise StopIteration

# have fill queue as a separate function
def fill_queue(queue, gen):
    while True:
        try:
            value = next(gen)
            queue.put(value)
        except StopIteration: # assumes the given data_loader_list is an iterator
            break
    print('stopping')


gen = iter(range(70))

qg = QueueGenerator(gen)


for val in qg:
    print(val)
# test if it works several times:
for val in qg:
    print(val)

我认为您要解决的下一个问题是让数据加载器列表在每个单独的过程中提供新的信息。但既然你没有提供任何相关信息,我也帮不了你。但是,上面确实提供了一种让进程填充队列的方法,然后将队列作为迭代器传递出去

我不太清楚你为什么在
下一个\uuuuuuuuuuuu下一步\uuuuuu
应返回值,而不是生成器对象

下面是一种简单的方法,可以将并行函数的结果作为生成器返回。它可能满足您的特定要求,也可能不满足您的特定要求,但可以根据您的需要进行调整。它将继续处理数据加载器列表,直到用尽为止。例如,与始终在
队列中保留4个项目相比,这可能会占用大量内存

将多处理导入为mp
def读取线(数据加载器):
从时间上导入睡眠
睡眠(2)
return f'do something with{data_loader}'
def生成(数据加载程序列表):
将mp.池(4)作为池:
对于pool.imap中的结果(读取行、数据加载器列表):
产量结果
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
数据加载程序列表=[i代表范围(15)中的i]
结果生成器=生成生成器(数据加载器列表)
打印(类型(结果生成器))
对于结果_生成器中的i:
印刷品(一)

使用
imap
意味着可以在生成结果时对其进行处理
map
map\u async
将在for循环中阻塞,直到所有结果就绪。更多信息,请参见。

不太清楚您为什么要在
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<代码>\uuuuuuuuuuuu下一步\uuuuuu
应返回值,而不是生成器对象

下面是一种简单的方法,可以将并行函数的结果作为生成器返回。它可能满足您的特定要求,也可能不满足您的特定要求,但可以根据您的需要进行调整。它将继续处理数据加载器列表,直到用尽为止。例如,与始终在
队列中保留4个项目相比,这可能会占用大量内存

将多处理导入为mp
def读取线(数据加载器):
从时间上导入睡眠
睡眠(2)
return f'do something with{data_loader}'
def生成(数据加载程序列表):
将mp.池(4)作为池:
对于pool.imap中的结果(读取行、数据加载器列表):
产量结果
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
数据加载程序列表=[i代表范围(15)中的i]
结果生成器=生成生成器(数据加载器列表)
打印(类型(结果生成器))
对于结果_生成器中的i:
印刷品(一)

使用
imap
意味着可以在生成结果时对其进行处理
map
map\u async
将在for循环中阻塞,直到所有结果就绪。有关详细信息,请参阅。

看起来不错。我今天就试试,非常感谢。data_loader对象返回3D图像的随机裁剪,因此它们应该在每个过程中提供新信息。工作起来很有魅力。谢谢你的快速解决方案。我遇到的下一个问题是,进程返回相同的图像裁剪,因为np.random.randint()根据时间生成一个伪随机数,并且进程同步运行。因此,必须为每个rng设置不同的种子,如中所述。我今天就试试,非常感谢。data_loader对象返回3D图像的随机裁剪,因此它们应该在每个过程中提供新信息。工作起来很有魅力。谢谢你的快速解决方案。我遇到的下一个问题是,进程返回相同的图像裁剪,因为np.random.randint()根据时间生成一个伪随机数,并且进程同步运行。因此,必须为每个rng设置不同的种子,如中所述