我正在使用多处理模块来分割一个非常大的任务。它在大多数情况下都有效,但我必须在设计中遗漏一些明显的东西,因为这样我很难有效地判断所有数据的处理时间。
我有两个独立的任务;一个喂另一个。我想这是生产者/消费者的问题。我在所有进程之间使用共享队列,生成器填充队列,消费者从队列中读取并进行处理。问题是数据量有限,所以在某些时候每个人都需要知道所有数据都已经处理好,所以系统可以优雅地关闭。
使用map_async()函数似乎是有意义的,但由于生产者正在填充队列,我不知道前面的所有项目,所以我必须进入while循环并使用apply_async()并尝试检测何时完成某些超时...丑陋。
我觉得我错过了一些明显的东西。如何更好地设计?
PRODCUER
class ProducerProcess(multiprocessing.Process):
def __init__(self, item, consumer_queue):
self.item = item
self.consumer_queue = consumer_queue
multiprocessing.Process.__init__(self)
def run(self):
for record in get_records_for_item(self.item): # this takes time
self.consumer_queue.put(record)
def start_producer_processes(producer_queue, consumer_queue, max_running):
running = []
while not producer_queue.empty():
running = [r for r in running if r.is_alive()]
if len(running) < max_running:
producer_item = producer_queue.get()
p = ProducerProcess(producer_item, consumer_queue)
p.start()
running.append(p)
time.sleep(1)
CONSUMER
def process_consumer_chunk(queue, chunksize=10000):
for i in xrange(0, chunksize):
try:
# don't wait too long for an item
# if new records don't arrive in 10 seconds, process what you have
# and let the next process pick up more items.
record = queue.get(True, 10)
except Queue.Empty:
break
do_stuff_with_record(record)
MAIN
if __name__ == "__main__":
manager = multiprocessing.Manager()
consumer_queue = manager.Queue(1024*1024)
producer_queue = manager.Queue()
producer_items = xrange(0,10)
for item in producer_items:
producer_queue.put(item)
p = multiprocessing.Process(target=start_producer_processes, args=(producer_queue, consumer_queue, 8))
p.start()
consumer_pool = multiprocessing.Pool(processes=16, maxtasksperchild=1)
这里是俗气的地方。我不能使用map,因为要消耗的列表正在同时填充。所以我必须进入while循环并尝试检测超时。当生产者仍然试图填充它时,consumer_queue可能变空,所以我不能只检测一个空队列就退出了。
timed_out = False
timeout= 1800
while 1:
try:
result = consumer_pool.apply_async(process_consumer_chunk, (consumer_queue, ), dict(chunksize=chunksize,))
if timed_out:
timed_out = False
except Queue.Empty:
if timed_out:
break
timed_out = True
time.sleep(timeout)
time.sleep(1)
consumer_queue.join()
consumer_pool.close()
consumer_pool.join()
我想也许我可以获取()主线程中的记录并将这些记录传递给消费者而不是传递队列,但我认为我最终会遇到同样的问题。我仍然需要运行while循环并使用apply_async()提前感谢您的任何建议!
答案 0 :(得分:2)
您可以使用manager.Event
表示工作结束。此事件可以在所有进程之间共享,然后当您从主进程发出信号时,其他工作人员可以正常关闭。
while not event.is_set():
...rest of code...
因此,您的消费者会等待设置事件并在设置完成后处理清理。
要确定何时设置此标志,您可以在生产者线程上执行join
,当这些完成后,您可以加入使用者线程。
答案 1 :(得分:0)
我强烈建议使用SimPy代替多进程/线程来执行离散事件模拟。