簡體   English   中英

關閉無限異步生成器

[英]Shutdown infinite async generator

可再現的錯誤

我試圖在此處的在線REPL中重現該錯誤。 但是,它與我的實際代碼(在async for response in position_stream() ,我async for response in position_stream()而不是在REPL for position in count()中的async for response in position_stream()實現的方式(以及行為)並不完全相同。

關於我的實際實施的更多細節

我在某個地方定義了這樣的協程:

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()

其中position_stream是無限的(或可能非常持久)。 我從這樣的示例代碼中使用它:

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

print_altitude()在循環上運行:

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

那很好。 現在,在某個時候,我想關閉來自調用方的流。 我以為我可以只運行asyncio.ensure_future(loop.shutdown_asyncgens())並等待我上面的finally關閉被調用,但這沒有發生。

相反,我收到有關未檢索到的異常的警告:

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

為什么會這樣,又如何使所有異步生成器實際上都關閉(並運行其finally子句)?

首先,如果您stop循環,則協程將沒有機會正常關閉。 基本上,調用close意味着不可逆轉地破壞循環。

如果您不關心這些正在運行的任務會發生什么,只需將其全部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