[英]Multiprocessing - producer/consumer design
我正在使用多處理模塊來拆分一個非常大的任務。 它在大多數情況下都有效,但我的設計肯定遺漏了一些明顯的東西,因為這樣我很難有效地判斷所有數據何時已被處理。
我有兩個單獨的任務在運行; 一個喂養另一個。 我想這是一個生產者/消費者問題。 我在所有進程之間使用共享隊列,其中生產者填充隊列,消費者從隊列中讀取並進行處理。 問題是數據量是有限的,所以在某個時候每個人都需要知道所有數據都已處理完畢,以便系統可以正常關閉。
使用 map_async() 函數似乎很有意義,但由於生產者正在填滿隊列,我不知道前面的所有項目,所以我必須進入一個 while 循環並使用 apply_async()並嘗試檢測什么時候一切都完成了某種超時......丑陋。
我覺得我錯過了一些明顯的東西。 如何更好地設計?
制作人
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)
消費者
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)
主要的
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)
這就是它變得俗氣的地方。 我不能使用地圖,因為要消耗的列表同時被填滿。 所以我必須進入一個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()
我想也許我可以在主線程中 get() 記錄並將它們傳遞給消費者而不是傳遞隊列,但我認為我最終會遇到同樣的問題。 我仍然需要運行一個 while 循環並使用 apply_async() 提前感謝您的任何建議!
您可以使用manager.Event
來表示工作結束。 此事件可以在您的所有進程之間共享,然后當您從主進程發出信號時,其他工作人員可以正常關閉。
while not event.is_set():
...rest of code...
因此,您的消費者將等待事件設置並在設置后處理清理。
要確定何時設置此標志,您可以在生產者線程上進行join
,當這些都完成后,您可以在消費者線程上進行連接。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.