【问题标题】:Schedule task to running event loop from synchronous code从同步代码安排任务到运行事件循环
【发布时间】:2018-01-27 18:52:27
【问题描述】:

考虑这个程序,其中主循环和停止它的协程实际上是由我正在使用的库实现的。

import asyncio
import signal

running = True

async def stop():
    global running
    print("setting false")
    running = False
    await asyncio.sleep(3)
    print("reached end")

async def mainloop():
    while running:
        print("loop")
        await asyncio.sleep(1)

def handle_signal():
    loop.create_task(stop())

loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, handle_signal)
loop.run_until_complete(mainloop())
loop.close()

当程序收到信号时,我需要调用停止协程来停止主循环。虽然在使用asyncio.BaseEventLoop.create_task 调度停止协程时,它首先停止了停止事件循环的主循环,并且停止协程无法完成:

$ ./test.py 
loop
loop
loop
^Csetting false
Task was destroyed but it is pending!
task: <Task pending coro=<stop() done, defined at ./test.py:7> wait_for=<Future pending cb=[Task._wakeup()]>>

如何将协程添加到正在运行的事件循环中,同时让事件循环等待完成?

【问题讨论】:

    标签: python python-asyncio coroutine


    【解决方案1】:

    正如您所发现的,问题在于事件循环只等待mainloop() 完成,而留下stop() 待处理,asyncio 正确地抱怨了这一点。

    如果handle_signal 和顶层代码在您的控制之下,您可以轻松地将循环直到mainloop 替换为循环直到自定义协程完成。这个协程会调用mainloop,然后等待清理代码完成:

    # ... omitted definition of mainloop() and stop()
    
    # list of tasks that must be waited for before we can actually exit
    _cleanup = []
    
    async def run():
        await mainloop()
        # wait for all _cleanup tasks to finish
        await asyncio.wait(_cleanup)
    
    def handle_signal():
        # schedule stop() to run, and also add it to the list of
        # tasks run() must wait for before it is done
        _cleanup.append(loop.create_task(stop()))
    
    loop = asyncio.get_event_loop()
    loop.add_signal_handler(signal.SIGINT, handle_signal)
    loop.run_until_complete(run())
    loop.close()
    

    另一个选项,不需要新的run() 协程(但仍需要修改后的handle_signal),是在mainloop 完成后发出第二个run_until_complete()

    # handle_signal and _cleanup defined as above
    
    loop = asyncio.get_event_loop()
    loop.add_signal_handler(signal.SIGINT, handle_signal)
    loop.run_until_complete(mainloop())
    if _cleanup:
        loop.run_until_complete(asyncio.wait(_cleanup))
    loop.close()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-10
      • 2023-03-04
      • 2018-04-10
      • 1970-01-01
      • 1970-01-01
      • 2016-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多