asyncio queue.get()卡住了

时间:2017-08-06 22:09:35

标签: python asynchronous multiprocessing python-3.6

我正在开发一个需要异步执行某些任务的项目,但我只能使用最少量的额外子进程,因此我决定使用2个进程:dispatcher_pworker_p 。由于我正在使用异步库,因此我有两个异步任务:async_workerasync_task。该计划的工作原理如下:

  1. worker_p使用单个任务启动事件循环:async_worker
  2. dispatcher_p队列上等待传入数据
  3. dispatcher_p通过put_nowait()
  4. async_queue 添加数据 正在等待 async_queue
  5. async_worker获取数据并使用async_task启动任务并调用 task_done()
  6. 为简单起见async_task只需睡3秒钟即可退出。
  7. 为简单起见,我们删除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如何在多个进程中工作。

1 个答案:

答案 0 :(得分:1)

记录

此脚本的设计有缺陷!

注意事项:

  1. 该脚本创建2个子进程,并将其传递给mp Queue和async Queue的引用。
  2. 即使他们通过python的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()