簡體   English   中英

如何使用 Python 多處理池永遠消耗隊列中的項目

[英]How to use Python multiprocessing Pool to consume items from queue forever

我正在嘗試創建一個 worker 來監聽 http 請求並將作業 ID 添加到隊列中。 為此,我正在使用 Python 的內置多處理模塊。

我需要一個帶有幾個進程的池,這些進程將處理來自隊列和重生的作業。 進程必須重新啟動,因為在某些情況下作業處理會導致 memory 泄漏。 池應該永遠運行,因為項目將動態添加到隊列中。

問題是我的池在完成后不會重生工作人員。

我怎樣才能使用池來實現這一目標? 我希望它永遠運行,消耗隊列中的項目並在每個任務后重生孩子。

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from multiprocessing import Pool, SimpleQueue, current_process

queue = SimpleQueue()

def do_something(q):
    worker_id = current_process().pid
    print(f"Worker {worker_id} spawned")
    item_id = q.get()
    print(f"Worker {worker_id} received id: {item_id}")
    # long_term_operation_that_leaks_memory(item_id)
    # print(f"Worker {worker_id} completed id: {item_id}")

def main():
    with Pool(
        processes=2, initializer=do_something, initargs=(queue,), maxtasksperchild=1
    ):
        queue.put("a")
        queue.put("b")
        queue.put("c")
        server_address = ("", 8000)
        httpd = ThreadingHTTPServer(server_address, BaseHTTPRequestHandler)
        try:
            httpd.serve_forever()
        except (KeyboardInterrupt, SystemExit):
            pass

if __name__ == "__main__":
    main()

我嘗試使用initializermaxtasksperchild但它不起作用。

我知道我可以使用 map 將新進程添加到池中,但我沒有未來無限可能任務的 map。 我認為initializer應該負責所有新任務。 但我不知道如何強迫它永遠運行並重生。

在我的代碼示例中,“c”項目從未被處理過。 因此,如果我添加 http 邏輯來放置更多項目,它也不會起作用。 在此代碼中添加 http 邏輯不是我的問題的必要部分,但歡迎任何提示。

謝謝!

編輯:

我決定在這種情況下使用 Pool 的原因是官方文檔說:

Pool 中的工作進程通常在 Pool 的工作隊列的整個持續時間內都存在。 在其他系統(例如 Apache、mod_wsgi 等)中發現的一種釋放 worker 持有的資源的常見模式是允許池中的 worker 在退出、清理和生成新進程之前僅完成一定數量的工作更換舊的。 Pool 的 maxtasksperchild 參數向最終用戶公開了此功能。

我的目標:

  • 項目將通過 http 請求動態添加到隊列中
  • 游泳池將永遠存在
  • 工作進程將只執行隊列中的一項任務並將重新生成

為什么我只使用了 2 個進程?

進程數不會是無限的,用 2 個進程而不是 5 個或 10 個進程來測試我的示例很容易。

為什么我手動放置 3 個項目? 這是為了示例目的,在實際解決方案中,所有項目都將動態添加,因此無法循環遍歷它們或對它們使用 map。

您對池初始化程序所做的事情是最不尋常的。 這樣的初始化程序為每個池進程運行,並用於初始化該進程(例如,設置全局變量),以便它能夠運行提交的任務 多處理池實現了一個隱藏的任務隊列,用於保存提交的等待可用池進程處理的任務。 您的初始化程序代碼只能執行一個准任務(我保留術語任務以“正常”方式提交給處理池的工作)然后它返回。 也就是說,您將 3 個項目放在隊列中,但您只有 2 個池進程從隊列中獲取一個項目,處理它然后返回。 這對我來說沒有任何意義。

您的代碼沒有顯示 HTTP 服務器與多處理池中正在運行的任務之間的關系,我不會猜測那可能是什么。 所以我只會展示使用池的更標准的方法。 我刪除了maxtasksperchild參數,因為它僅在您的池正在執行添加到任務隊列的“正常”任務時才相關,例如,使用apply_asyncmap方法。 因此它沒有在您的代碼中完成任何事情。

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from multiprocessing import Pool, current_process


def do_something(item_id):
    worker_id = current_process().pid
    print(f"Worker {worker_id} received id: {item_id}")
    # long_term_operation_that_leaks_memory(item_id)
    # print(f"Worker {worker_id} completed id: {item_id}")

def main():
    # Why only 2 processes in the pool?:
    pool = Pool(processes=2)
    pool.apply_async(do_something, args=('a',))
    pool.apply_async(do_something, args=('b',))
    pool.apply_async(do_something, args=('c',))
    server_address = ("", 8000)
    httpd = ThreadingHTTPServer(server_address, BaseHTTPRequestHandler)
    try:
        httpd.serve_forever()
    except (KeyboardInterrupt, SystemExit):
        pass
    # Wait for submitted tasks to complete:
    pool.close()
    pool.join()

if __name__ == "__main__":
    main()

印刷:

Worker 15560 received id: a
Worker 8132 received id: b
Worker 15560 received id: c

在我看來,也許你真的不需要這里的pool ,也許可以為每個任務創建一個新的Process 如果你想限制同時存在多少任務,你可以使用Semaphore來限制進程創建,並在每個任務完成之前釋放該信號量:

from multiprocessing import Process, BoundedSemaphore
from time import sleep

def do_work(A, B):
    sleep(.4)
    print(A, B)

def worker(sema, *args):
    try:
        do_work(*args)
    finally:
        sema.release() #allow a new process to be started now that this one is exiting

def main():
    tasks = zip(range(65,91), bytes(range(65,91)).decode())
    sema = BoundedSemaphore(4) #only every 4 workers at a time
    procs = []
    for arglist in tasks:
        sema.acquire() #wait to start until another process is finished
        procs.append(Process(target=worker, args=(sema, *arglist)))
        procs[-1].start()

        #cleanup completed processes
        while not procs[0].is_alive():
            procs.pop(0)
    for p in procs:
        p.join() #wait for any remaining tasks
    print("done")

if __name__ == "__main__":
    main()

暫無
暫無

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

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