我正在开发一个需要异步执行某些任务的项目,但我只能使用最少量的额外子进程,因此我决定使用2个进程:dispatcher_p
和worker_p
。由于我正在使用异步库,因此我有两个异步任务:async_worker
和async_task
。该计划的工作原理如下:
worker_p
使用单个任务启动事件循环:async_worker
dispatcher_p
在队列上等待传入数据dispatcher_p
通过put_nowait()
async_worker
获取数据并使用async_task
启动任务并调用 task_done() async_task
只需睡3秒钟即可退出。为简单起见,我们删除async_worker
应该开始的子任务,给我们留下以下代码:
import multiprocessing as mp
import asyncio
async def task_worker(aq):
while True:
task = await aq.get()
if task is not None:
await asyncio.sleep(3)
aq.task_done()
else:
break
def dispatcher_p(ev_l, q, async_q):
asyncio.set_event_loop(ev_l)
while True:
task = q.get()
if task is not None:
async_q.put_nowait(task)
else:
async_q.put(None)
break
def worker_p(ev_l, aq):
asyncio.set_event_loop(ev_l)
ev_l.run_until_complete(asyncio.gather(task_worker(aq)))
ev_l.close()
q = mp.Queue()
def put_task(data):
q.put(data)
def init():
event_loop = asyncio.get_event_loop()
aq = asyncio.Queue()
p1 = mp.Process(target=worker_p, args=(event_loop, aq,))
p2 = mp.Process(target=dispatcher_p, args=(event_loop, q, aq))
p1.start()
p2.start()
# Test
put_task("hi")
put_task("bye")
put_task("test")
put_task(None)
if __name__ == '__main__':
init()
问题在于,即使task_worker
在event_loop中运行,它也会在task = await aq.get()
处冻结。为什么会这样?我仍然不明白asyncio如何在多个进程中工作。
答案 0 :(得分:1)
记录
此脚本的设计有缺陷!
注意事项:
id()
报告了相同的对象ID,但它们似乎在不同的内存环境中运行,因此无法彼此通信 await aq.get()
被卡住的原因是,即使我们将元素添加到传递给进程1的异步队列中,进程2中的异步队列也无法看到/得到通知。
解决方案是使用多进程队列进行进程间通信,并在同一进程内使用异步队列。跨进程使用异步队列无法按预期方式工作,而且很糟!
通过将异步队列的状态打印到控制台,您可以更清楚地看到这一点:
import multiprocessing as mp
import asyncio
async def task_worker(aq:asyncio.Queue):
print('ASYNC: Started')
while True:
print(f'ASYNC: Get info from async queue. {aq.qsize()}')
task = await aq.get()
if task is not None:
print(f'ASYNC: Work on task asynchronously: {task}')
await asyncio.sleep(3)
aq.task_done()
else:
print('ASYNC: Sentinel, end async worker')
break
print('ASYNC: Task done?')
def dispatcher_p(ev_l, q, async_q:asyncio.Queue):
print('DISPATCHER: Started')
asyncio.set_event_loop(ev_l)
while True:
print('DISPATCHER: Get task from mp queue')
task = q.get()
if task is not None:
print(f'DISPATCHER: Work on task: {task}')
async_q.put_nowait(task)
print(f'DISPATCHER: Async queue size: {async_q.qsize()}')
else:
print('DISPATCHER: Sentinel, send None')
async_q.put_nowait(None)
break
print('DISPATCHER: Dispatcher ended')
def worker_p(ev_l, aq):
print('WORKER: Started')
asyncio.set_event_loop(ev_l)
print('WORKER: Wait until loop ends')
ev_l.run_until_complete(asyncio.gather(task_worker(aq)))
print('WORKER: Loop ended')
ev_l.close()
print('WORKER: Loop closed')
print('WORKER: Worker ended')
q = mp.Queue()
def put_task(data):
q.put(data)
def init():
event_loop = asyncio.get_event_loop()
aq = asyncio.Queue()
p1 = mp.Process(target=worker_p, args=(event_loop, aq,))
p2 = mp.Process(target=dispatcher_p, args=(event_loop, q, aq))
p1.start()
p2.start()
# Test
put_task("hi")
put_task("bye")
put_task("test")
put_task(None)
if __name__ == '__main__':
init()