簡體   English   中英

多處理 - 生產者/消費者設計

[英]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 ,當這些都完成后,您可以在消費者線程上進行連接。

我強烈推薦SimPy而不是多進程/線程來進行離散事件模擬。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM