简体   繁体   English

关闭无限异步生成器

[英]Shutdown infinite async generator

Reproducible error 可再现的错误

I tried to reproduce the error in an online REPL here . 我试图在此处的在线REPL中重现该错误。 However, it is not exactly the same implementation (and hence behavior) as my real code (where I do async for response in position_stream() , instead of for position in count() in the REPL). 但是,它与我的实际代码(在async for response in position_stream() ,我async for response in position_stream()而不是在REPL for position in count()中的async for response in position_stream()实现的方式(以及行为)并不完全相同。

More details on my actual implementation 关于我的实际实施的更多细节

I define somewhere a coroutine like so: 我在某个地方定义了这样的协程:

async def position(self):
    request = telemetry_pb2.SubscribePositionRequest()
    position_stream = self._stub.SubscribePosition(request)

    try:
        async for response in position_stream:
            yield Position.translate_from_rpc(response)
    finally:
        position_stream.cancel()

where position_stream is infinite (or possibly very long lasting). 其中position_stream是无限的(或可能非常持久)。 I use it from an example code like this: 我从这样的示例代码中使用它:

async def print_altitude():
    async for position in drone.telemetry.position():
        print(f"Altitude: {position.relative_altitude_m}")

and print_altitude() is run on the loop with: print_altitude()在循环上运行:

asyncio.ensure_future(print_altitude())
asyncio.get_event_loop().run_forever()

That works well. 那很好。 Now, at some point, I'd like to close the stream from the caller. 现在,在某个时候,我想关闭来自调用方的流。 I thought that I could just run asyncio.ensure_future(loop.shutdown_asyncgens()) and wait for my finally close above to get called, but it doesn't happen. 我以为我可以只运行asyncio.ensure_future(loop.shutdown_asyncgens())并等待我上面的finally关闭被调用,但这没有发生。

Instead, I receive a warning on an unretrieved exception: 相反,我收到有关未检索到的异常的警告:

Task exception was never retrieved
future: <Task finished coro=<print_altitude() done, defined at [...]

Why is that, and how can I make it such that all my async generators actually get closed (and run their finally clause)? 为什么会这样,又如何使所有异步生成器实际上都关闭(并运行其finally子句)?

First of all, if you stop a loop, none of your coroutines will have a chance to shut down properly. 首先,如果您stop循环,则协程将没有机会正常关闭。 Calling close basically means irreversibly destroying the loop. 基本上,调用close意味着不可逆转地破坏循环。

If you do not care what happens to those running tasks, you can simply cancel them all, this will stop asynchronous generators as well: 如果您不关心这些正在运行的任务会发生什么,只需将其全部cancel即可,这也会停止异步生成器:

import asyncio
from contextlib import suppress


async def position_stream():
    while True:
        await asyncio.sleep(1)
        yield 0

async def print_position():
    async for position in position_stream():
        print(f'position: {position}')

async def cleanup_awaiter():
    await asyncio.sleep(3)
    print('cleanup!')

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        asyncio.ensure_future(print_position())
        asyncio.ensure_future(print_position())
        loop.run_until_complete(cleanup_awaiter())
        # get all running tasks:
        tasks = asyncio.gather(*asyncio.Task.all_tasks())
        # schedule throwing CancelledError into the them:
        tasks.cancel()
        # allow them to process the exception and be cancelled:
        with suppress(asyncio.CancelledError):
            loop.run_until_complete(tasks)
    finally:
        print('closing loop')
        loop.close()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM