可重复的多处理队列不退出

时间:2014-10-16 13:05:23

标签: python queue multiprocessing

import multiprocessing.queues as queues
import multiprocessing
class I(queues.Queue):
    def __init__(self, maxsize=0):
        super(I, self).__init__(maxsize)
        self.length = 0 

    def __iter__(self):
        return self

    def put(self, obj, block=True, timeout=None):
        super(I, self).put(obj,block,timeout)
        self.length += 1

    def get(self, block = True, timeout = None):
        self.length -= 1
        return super(I, self).get(block, timeout)

    def __len__(self):
        return self.length

    def next(self):
        item = self.get()
        if item == 'Done':
            raise StopIteration
        return item


def thisworker(item):
    print 'got this item: %s' % item
    return item

q=I()

q.put(1)
q.put('Done')

the_pool = multiprocessing.Pool(1)
print the_pool.map(thisworker, q)

我试图创建一个可迭代队列以用于多处理池映射。 我们的想法是,函数thisworker会将某些项添加到队列中,直到满足条件,然后在放入“完成”后退出。在队列中(我还没有在这个代码中完成它)

但是,这段代码永远不会完成,它总是挂断。

我无法调试真正的原因。 请求帮助

PS:我已使用self.length,因为从map_async下调用的the_pool.map方法需要使用iterable的长度来形成变量:chunksize ,这将用于从池中获取任务。

1 个答案:

答案 0 :(得分:1)

问题在于您将'Done'视为Queue中的特殊案例项,这表示迭代应该停止。因此,如果您使用示例的for循环遍历Queue,则返回的所有内容都是1。但是,您声称Queue的长度为2.这会搞砸map代码,该代码依赖于该长度来准确表示迭代中的项目数量为了知道所有结果何时从工人那里返回:

class MapResult(ApplyResult):

    def __init__(self, cache, chunksize, length, callback):
        ApplyResult.__init__(self, cache, callback)
        ...
        # _number_left is used to know when the MapResult is done
        self._number_left = length//chunksize + bool(length % chunksize)

因此,您需要使长度实际上准确。您可以通过以下几种方式实现这一目标,但我建议您不要将哨兵加载到Queue中,而是使用get_nowait代替:

import multiprocessing.queues as queues
import multiprocessing
from Queue import Empty

class I(queues.Queue):
    def __init__(self, maxsize=0):
        super(I, self).__init__(maxsize)
        self.length = 0 

    ... <snip>

    def next(self):
        try:
            item = self.get_nowait()
        except Empty:
            raise StopIteration
        return item


def thisworker(item):
    print 'got this item: %s' % item
    return item

q=I()

q.put(1)

the_pool = multiprocessing.Pool(1)
print the_pool.map(thisworker, q)

另外,请注意,这种方法不是安全的。 length属性只有在您从put Queue进入put时才会正确,然后再将Queue发送给multiprocessing.queues.Queue工人流程。它还没有在调整导入和实现的情况下在Python 3中工作,因为multiprocessing.queues.Queue的构造函数已经改变。

我建议使用iter内置来迭代Queue,而不是继承q = multiprocessing.Queue() q.put(1) q.put(2) q.put(None) # None is our sentinel, you could use 'Done', if you wanted the_pool.map(thisworker, iter(q.get, None)) # This will call q.get() until None is returned

imap

这适用于所有版本的Python,代码少得多,并且是过程安全的。

修改

根据您在我的回答评论中提到的要求,我认为您最好使用map代替Queue,这样您就不需要知道了imap的长度。实际情况是,您无法准确地确定这一点,事实上,当您进行迭代时,长度可能会最终增长。如果您只使用import multiprocessing class I(object): def __init__(self, maxsize=0): self.q = multiprocessing.Queue(maxsize) def __getattr__(self, attr): if hasattr(self.q, attr): return getattr(self.q, attr) def __iter__(self): return self def next(self): item = self.q.get() if item == 'Done': raise StopIteration return item def thisworker(item): if item == 1: q.put(3) if item == 2: q.put('Done') print 'got this item: %s' % item return item q=I() q.put(1) q.put(2) q.put(5) the_pool = multiprocessing.Pool(2) # 2 workers print list(the_pool.imap(thisworker, q)) ,那么执行与原始方法类似的操作将会正常工作:

got this item: 1
got this item: 5
got this item: 3
got this item: 2
[1, 2, 5, 3]

输出:

iter(q.get, <sentinel>)

我摆脱了担心长度的代码,并使用委托代替继承,以获得更好的Python 3.x兼容性。

请注意,只要您使用imap代替map,我使用{{1}}的原始建议仍可在此处使用。