Python 如何";批量写入";使用多处理从输出队列?

Python 如何";批量写入";使用多处理从输出队列?,python,multiprocessing,Python,Multiprocessing,假设我有以下多处理结构: import multiprocessing as mp def worker(working_queue, output_queue): while True: if working_queue.empty() == True: break else: picked = working_queue.get() res_item = "Number " +

假设我有以下多处理结构:

import multiprocessing as mp
def worker(working_queue, output_queue):
    while True:
        if working_queue.empty() == True:
            break 
        else:
            picked = working_queue.get()
            res_item = "Number " + str(picked)
            output_queue.put(res_item)
    return

if __name__ == '__main__':
    static_input = xrange(100)    
    working_q = mp.Queue()
    output_q = mp.Queue()
    results_bank = []
    for i in static_input:
        working_q.put(i)
    processes = [mp.Process(target=worker,args=(working_q, output_q)) for i in range(2)]
    for proc in processes:
        proc.start()
    for proc in processes:
        proc.join()
    results_bank = []
    while True:
       if output_q.empty() == True:
           break
       results_bank.append(output_q.get_nowait())
    if len(results_bank) == len(static_input):
        print "Good run"
    else:
        print "Bad run"
我的问题:当工作队列仍在“工作”(或至少尚未完成)时,如何“批处理”将结果写入单个文件

注意:我的实际数据结构对相对于输入的无序结果不敏感(尽管我的示例使用整数)


另外,我认为从输出队列进行批/集写入是最佳实践,而不是从不断增长的结果库对象进行。然而,我对依赖于这两种方法的解决方案持开放态度。我是多道处理新手,因此不确定该问题的最佳实践或最有效的解决方案。

没有像“批处理
q.get
”这样的操作。但是,将一批项目而不是一个项目一个项目地放入/弹出是一种很好的做法

这正是它的参数
chunksize
:)的作用

为了尽快写入输出,有一个函数返回一个iterable而不是list

def work(item):
    return "Number " + str(item)

import multiprocessing
static_input = range(100)
chunksize = 10
with multiprocessing.Pool() as pool:
    for out in pool.imap_unordered(work, static_input, chunksize):
        print(out)

如果您希望使用
mp.Process
es和
mp.Queue
s,这里有一种成批处理结果的方法。主要思想在
writer
函数中,如下所示:

import itertools as IT
import multiprocessing as mp
SENTINEL = None
static_len = 100

def worker(working_queue, output_queue):
    for picked in iter(working_queue.get, SENTINEL):
        res_item = "Number {:2d}".format(picked)
        output_queue.put(res_item)

def writer(output_queue, threshold=10):
    result_length = 0
    items = iter(output_queue.get, SENTINEL)
    for batch in iter(lambda: list(IT.islice(items, threshold)), []):
        print('\n'.join(batch))
        result_length += len(batch)
    state = 'Good run' if result_length == static_len else 'Bad run'
    print(state)

if __name__ == '__main__':
    num_workers = 2

    static_input = range(static_len)
    working_q = mp.Queue()
    output_q = mp.Queue()

    writer_proc = mp.Process(target=writer, args=(output_q,))
    writer_proc.start()

    for i in static_input:
        working_q.put(i)

    processes = [mp.Process(target=worker, args=(working_q, output_q)) 
                 for i in range(num_workers)]
    for proc in processes:
        proc.start()
        # Put SENTINELs in the Queue to tell the workers to exit their for-loop
        working_q.put(SENTINEL)
    for proc in processes:
        proc.join()

    output_q.put(SENTINEL)
    writer_proc.join()

当传递两个参数时,需要一个callable和一个sentinel:
iter(可呼叫,哨兵)
。可调用函数(即函数)被反复调用,直到它返回一个等于
sentinel
的值。所以

items = iter(output_queue.get, SENTINEL)
items
定义为一个iterable,当对其进行迭代时,它将从
output\u队列返回项
直到
output\u queue.get()
返回
SENTINEL

循环的

for batch in iter(lambda: list(IT.islice(items, threshold)), []):
重复调用lambda函数,直到返回空列表。调用lambda函数时,会从iterable
items
返回最多
阈值的项数列表。因此,这是一个“按n项分组而不填充”的成语。有关此成语的详细信息,请参见


请注意,测试
正在工作\u q.empty()
不是一个好的做法。这可能导致比赛状态。例如,假设在这些行上有2个
worker
进程,而
working_q
中只剩下1项:

def worker(working_queue, output_queue):
    while True:
        if working_queue.empty() == True:        <-- Process-1
            break 
        else:
            picked = working_queue.get()         <-- Process-2
            res_item = "Number " + str(picked)
            output_queue.put(res_item)
    return
def工作者(工作队列、输出队列):
尽管如此:

如果正在工作_queue.empty()==True:@martineau感谢您澄清“批处理”。我自己也打算这么做。似乎在
worker()
中可以有一个嵌套循环,它一直运行,直到
工作队列
被清空。这就是你所说的“批量写入”吗?不,我的数据需求更多的是“随着结果的积累,写出来。”比如说,用5个“集合”写出结果。根据我对您建议的理解,这将在工作队列的末尾写出结果,这基本上等同于上面示例中我的“成长对象”(结果库)。或者你的意思是说,当工作队列“清除”或“刷新”时,我可以将结果以集合的形式写出来?我的意思是后者(通过当时获取其中的所有内容来刷新)。当你积累了一批价值时,你可以把它们写出来。我想这基本上就是@Messa在回答中的建议。哦,好吧。正如我所说的,我对多处理还不太熟悉,所以现在还不确定队列是如何工作的。我知道的足够多,可以将结果放入一个专用队列中,但不知道如果我试图从输出队列写入,进程是否会“阻塞”或相互竞争,因为它仍在填充。请您进一步解释一下您从iter中的“for res_item”(output_queue.get,SENTINEL)开始的代码:“特别是,我不确定为什么(或如何)您的“if len(batch)>=threshold”语句在“if len(batch)”中“重复”。。。无论是否已达到阈值,您似乎都在扩展结果对象?不管阈值、批次等如何,打印都是好的。但理解“批次”长度何时等于阈值,以便用写入文件替换“打印”语句,这一点至关重要。@DVHughes:我在上面添加了一些解释。如果有任何不清楚的地方,请告诉我。好的!谢谢,我想这是你提到的“剩余”批次。只是想在添加任何write语句之前确认一下。如果len(批次)使您看起来像是在测试批次不是空的。。但实际上,您正在测试是否有任何剩余/剩余。相同的区别,但概念清晰是很好的,特别是对于其他人,随着时间的推移,他们会在这个问题/线程中发生。谢谢这很容易理解,并且与我在示例代码中基于队列的多进程方法一致。@DVHughes:我更改了上面的代码,以便我们迭代批处理,而不是迭代单个项目。这避免了剩余批处理的问题,并且(大的好处)意味着打印到屏幕或写入到文件两次。谢谢,这是真正干净的逻辑,从代码的概念上讲是清楚的。我认为你改进了你的答案。我在“批处理获取”中找到的唯一其他资源就是这样的: