Please explain “Task was destroyed but it is pending!”

前端 未结 4 811
难免孤独
难免孤独 2021-02-06 22:54

Python 3.4.2

I am learning asyncio and I use it to continously listen IPC bus, while gbulb listens to the dbus.

Some side notes:

So I created a fun

4条回答
  •  刺人心
    刺人心 (楼主)
    2021-02-06 23:00

    The problem comes from closing the loop immediately after cancelling the tasks. As the cancel() docs state

    "This arranges for a CancelledError to be thrown into the wrapped coroutine on the next cycle through the event loop."

    Take this snippet of code:

    import asyncio
    import signal
    
    
    async def pending_doom():
        await asyncio.sleep(2)
        print(">> Cancelling tasks now")
        for task in asyncio.Task.all_tasks():
            task.cancel()
    
        print(">> Done cancelling tasks")
        asyncio.get_event_loop().stop()
    
    
    def ask_exit():
        for task in asyncio.Task.all_tasks():
            task.cancel()
    
    
    async def looping_coro():
        print("Executing coroutine")
        while True:
            try:
                await asyncio.sleep(0.25)
            except asyncio.CancelledError:
                print("Got CancelledError")
                break
    
            print("Done waiting")
    
        print("Done executing coroutine")
        asyncio.get_event_loop().stop()
    
    
    def main():
        asyncio.async(pending_doom())
        asyncio.async(looping_coro())
    
        loop = asyncio.get_event_loop()
        for sig in (signal.SIGINT, signal.SIGTERM):
            loop.add_signal_handler(sig, ask_exit)
    
        loop.run_forever()
    
        # I had to manually remove the handlers to
        # avoid an exception on BaseEventLoop.__del__
        for sig in (signal.SIGINT, signal.SIGTERM):
            loop.remove_signal_handler(sig)
    
    
    if __name__ == '__main__':
        main()
    

    Notice ask_exit cancels the tasks but does not stop the loop, on the next cycle looping_coro() stops it. The output if you cancel it is:

    Executing coroutine
    Done waiting
    Done waiting
    Done waiting
    Done waiting
    ^CGot CancelledError
    Done executing coroutine
    

    Notice how pending_doom cancels and stops the loop immediately after. If you let it run until the pending_doom coroutines awakes from the sleep you can see the same warning you're getting:

    Executing coroutine
    Done waiting
    Done waiting
    Done waiting
    Done waiting
    Done waiting
    Done waiting
    Done waiting
    >> Cancelling tasks now
    >> Done cancelling tasks
    Task was destroyed but it is pending!
    task:  wait_for=>
    

提交回复
热议问题