asyncio код не работает как надо

У меня есть асинхронный код.

import asyncio
from time import time


async def say_yup():
    for i in range(10):
        await asyncio.sleep(1)
        print('yup')


async def gone(n, end_time):
    t = time()
    while True:
        if (time() - t) + n >= end_time:
            return
        await asyncio.sleep(n)
        print(f'{n}s gone')


async def main(n, t):
    tasks = [asyncio.create_task(say_yup()), asyncio.create_task(gone(n, t))]
    await asyncio.gather(*tasks)


asyncio.run(main(3, 10))

В нём я создал 2 корутины. Я хочу чтобы каждую секунду выводилось сообщение yup и каждые n секунд в течении какого-то времени выводилось:

f'{n}s gone'

В результате, передав в корутину "gone" 3 и 10, я хочу увидеть такой результат выполнения кода:

yup
yup
yup
3s gone
yup
yup
yup
3s gone
yup
yup
yup
3s gone
yup

А получаю такой:

yup
yup
3s gone
yup
yup
yup
3s gone
yup
yup
yup
3s gone
yup
yup

Пожалуйста, помогите исправить код.


Ответы (1 шт):

Автор решения: Roman Konoval

Когда вы используете sleep нет гарантии сколько точно корутина будет спать. Если две будут спать и проснутся точно в то же время, то какая-то одна из них будет продолжена первой. Если нужно упорядочить их выполнение более строго, то на время и sleep полагаться нельзя.

Т.е у вас требование не просто "каждые три секунды", а "каждые три секунды, но обязательно после того как выполнилось yup". В таком случае нужно делать явную синхронизацию используя механизмы синхронизации, например Event:

async def say_yup(event):
    for i in range(10):
        await asyncio.sleep(1)
        if i % 3 == 2:
            event.set()
        print('yup')


async def gone(event, n, end_time):
    t = time()
    while True:
        if (time() - t) + n >= end_time:
            return
        await event.wait()
        print(f'{n}s gone')
        event.clear()


async def main(n, t):
    event = asyncio.Event()
    tasks = [asyncio.create_task(say_yup(event)), asyncio.create_task(gone(event, n, t))]
    await asyncio.gather(*tasks)
→ Ссылка