【问题标题】:Asynchronously wait for multiprocessing Queue in main process在主进程中异步等待多处理队列
【发布时间】:2017-06-30 19:37:26
【问题描述】:

我有以下场景:多个工作进程将有关其当前状态的事件发送到事件调度程序。如果我们在主进程中,则该事件调度器需要处理所有事件,或者如果我们在工作进程中,则向主进程的事件调度器发出信号以处理这些事件。

这里的主要症结是事件处理也必须在主进程的主线程中,所以我不能只在线程内运行一个while True循环并等待来自worker的消息在那里处理。

所以我拥有的是这样的:

import asyncio
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import current_process, Process, Queue
from threading import current_thread
from time import sleep

def get_q(q):
    print("Waiting for the queue ({} / {})\n".format(current_thread().name, current_process().name))
    return q.get()

async def message_q(q):
    while True:
        f = loop.run_in_executor(None, get_q, q)

        await f

        if f.result() is None:
            print("Done")
            return;

        print("Got the result ({} / {})".format(current_thread().name, current_process().name))
        print("Result is: {}\n".format(f.result()))

async def something_else():
    while True:
        print("Something else\n")
        await asyncio.sleep(2)

def other_process(q):
    for i in range(5):
        print("Putting something in the queue ({})".format(current_process().name))
        q.put(i)
        sleep(1)

    q.put(None)

q = Queue()

Process(target=other_process, args=(q,), daemon=True).start()

loop = asyncio.get_event_loop()
loop.set_default_executor(ThreadPoolExecutor(max_workers=1))
asyncio.ensure_future(message_q(q))
asyncio.ensure_future(something_else())
loop.run_until_complete(asyncio.sleep(6))

other_process() 是一个示例性的工作进程,它使用队列向主进程发出信号,主进程运行事件循环来处理内容并等待队列上的任何数据。在实际情况下,该进程会向事件调度器发出信号,该事件调度器随后将处理队列消息,将消息传递给主进程事件调度器,但这里我将其简化了一点。

但是,我对此不太满意。一次又一次地将get_q() 提交给ThreadPoolExecutor 会产生更多的开销,并且不像一个长时间运行的线程那么干净。 await f 也不是最优的,一旦队列中没有更多数据就会阻塞,这会阻止事件循环退出。我的解决方法是在工作人员完成后发送None,如果None 在队列中,则退出message_q()。

有没有更好的方法来实现这个?性能非常重要,我想将 Queue 对象保留在事件调度程序的本地,而不是将其传递给管理工作进程的代码(或需要调用某种finalize() 方法)。

【问题讨论】:

    标签: python python-multiprocessing python-asyncio


    【解决方案1】:

    我现在将其实现为异步上下文管理器。上下文管理器调用

    asyncio.ensure_future(message_q())
    

    在其__aenter__() 方法中并在其__aexit__() 方法中将None 添加到队列中以关闭message_q() 中的无限循环。

    然后可以在围绕进程生成代码部分的async with 语句中使用上下文管理器,从而无需手动调用关闭方法。但是,在确保 message_q() 协程允许上下文管理器初始化队列侦听器之后,建议在 __aenter__() 方法内调用 await asyncio.sleep(0)。否则,message_q() 将不会立即被调用。这本身不是问题(因为无论如何队列已满),但它会延迟事件转发,直到代码中出现下一个await。

    应该使用ProcessPoolExecutor 和loop.run_in_executor() 生成进程,因此等待进程不会阻塞事件循环。

    除了使用队列之外,您可能还想使用 JoinableQueue 以确保在退出上下文管理器之前已处理所有事件。

    【讨论】:

    • 嗨@janek-bevendorff,您能否发布解决方案的最终代码,以便更容易理解您所做的事情?
    猜你喜欢
    • 2019-03-04
    • 1970-01-01
    • 1970-01-01
    • 2019-11-17
    • 2013-09-10
    • 1970-01-01
    • 2022-10-08
    • 2022-01-02
    • 1970-01-01
    相关资源
    最近更新 更多