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
,这将用于从池中获取任务。
答案 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}}的原始建议仍可在此处使用。