Разница между `asyncio.wait ([asyncio.sleep (5)])` и `asyncio.sleep (5)` - PullRequest
1 голос
/ 25 мая 2020

Не мог бы кто-нибудь объяснить, почему существует 5-секундная задержка между coro2 окончанием и coro1 окончанием?

Кроме того, почему нет такой задержки, если я заменяю asyncio.wait([asyncio.sleep(5)]) на asyncio.sleep(5)?

async def coro1():
    logger.info("coro1 start")
    await asyncio.wait([asyncio.sleep(5)])
    logger.info("coro1 finish")

async def coro2():
    logger.info("coro2 start")
    time.sleep(10)
    logger.info("coro2 finish")

async def main():
    await asyncio.gather(coro1(), coro2())

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
2020-05-25 12:44:56 coro1 start
2020-05-25 12:44:56 coro2 start
2020-05-25 12:45:06 coro2 finish
2020-05-25 12:45:11 coro1 finish

1 Ответ

3 голосов
/ 25 мая 2020

TL; DR: не используйте блокирующие вызовы, такие как time.sleep, в сопрограмме. Используйте asyncio.sleep для асинхронной паузы или событие l oop исполнитель , если блокирующий код должен быть запущен.


Использование asyncio.wait([thing]) добавляет уровень косвенности, выполнение thing в новом Будущем / Задаче. В то время как пустой await asyncio.sleep(5) выполняет спящий режим во время coro1, завернутый await asyncio.wait([asyncio.sleep(5)]) выполняет спящий после всех других запланированных на данный момент сопрограмм.

async def coro1():
    logger.info("coro1 start")
    await asyncio.sleep(5)   # started immediately
    logger.info("coro1 finish")

async def coro1():
    logger.info("coro1 start")
    await asyncio.wait([     # started immediately
        asyncio.sleep(5)     # started in new task
    ])
    logger.info("coro1 finish")

Поскольку coro2 использует блокируя time.sleep(10), он отключает событие l oop и все другие сопрограммы.

async def coro2():
    logger.info("coro2 start")
    time.sleep(10)           # nothing happens for 10 seconds
    logger.info("coro2 finish")

Это предотвращает запуск дальнейших Futures, включая новое будущее от asyncio.wait, и от возобновления, включая голый asyncio.sleep(5). В первом случае это означает, что asyncio.sleep запускается после , time.sleep завершается, поэтому для завершения требуется 10 + 5 секунд. В последнем случае это означает, что asyncio.sleep уже запущен, он просто не может завершить до 10 секунд, поэтому для завершения требуется max(10, 5) секунд.


Последовательно используйте asyncio.sleep, чтобы получить желаемую продолжительность. Если блокирующий код должен быть выполнен, пусть он запускается через исполнитель .

async def coro1w():
    print("coro1w start", time.asctime())
    await asyncio.wait([asyncio.sleep(5)])
    print("coro1w finish", time.asctime())

async def coro1b():
    print("coro1b start", time.asctime())
    await asyncio.sleep(5)
    print("coro1b finish", time.asctime())

async def coro2a():
    print("coro2a start", time.asctime())
    await asyncio.sleep(10)            # asynchronous sleep
    print("coro2a finish", time.asctime())

async def coro2t():
    print("coro2t start", time.asctime())
    loop = asyncio.get_running_loop()  # threaded sleep
    await loop.run_in_executor(None, lambda: time.sleep(10))
    print("coro2t finish", time.asctime())

async def main():
    await asyncio.gather(coro1w(), coro1b(), coro2a(), coro2t())

asyncio.run(main())
...