Задать вопрос
@to_east

Python вызов асинхронной функции с периодичностью?

Вобщем код:
import asyncio
from dataclasses import dataclass
import time
from datetime import datetime
from random import randint


@dataclass
class State:
    value: int


async def delayer(seconds: int):
    while True:
        interval = seconds * 1e+9
        delay = interval - (time.time_ns() % interval)
        yield await asyncio.sleep(delay * 1e-9)


async def update(state, interval):
    async for _ in delayer(interval):
        new_value = randint(1, 100)
        state.value += new_value
        print(f"{datetime.now()} update {state.value}")


async def main():
    state = State(0)

    asyncio.create_task(update(state, 5))

    while True:
        print(f"{datetime.now()} main {state.value}")
        await asyncio.sleep(.5)


if __name__ == '__main__':
    asyncio.run(main())

Казалось бы все вроде логично, но посмотрев на вывод, увидел вот это:
...
01. 2025-05-15 14:05:39.308481 main 391
02. 2025-05-15 14:05:39.811125 main 391
03. 2025-05-15 14:05:40.001140 update 436
04. 2025-05-15 14:05:40.315979 main 436
05. 2025-05-15 14:05:40.830168 main 436
06. 2025-05-15 14:05:41.330853 main 436
07. 2025-05-15 14:05:41.845188 main 436
08. 2025-05-15 14:05:42.346309 main 436
09. 2025-05-15 14:05:42.860398 main 436
10. 2025-05-15 14:05:43.361397 main 436
11. 2025-05-15 14:05:43.863503 main 436
12. 2025-05-15 14:05:44.364032 main 436
13. 2025-05-15 14:05:44.869065 main 436
14. 2025-05-15 14:05:44.994269 update 471
15. 2025-05-15 14:05:45.009148 update 523
16. 2025-05-15 14:05:45.382439 main 523
17. 2025-05-15 14:05:45.889243 main 523
18. 2025-05-15 14:05:46.395340 main 523
19. 2025-05-15 14:05:46.895351 main 523
20. 2025-05-15 14:05:47.396639 main 523
21. 2025-05-15 14:05:47.897962 main 523
22. 2025-05-15 14:05:48.401170 main 523
...

В 14 и 15-ых строках сработали вызовы без необходимой задержки, в 14-ой строке если посмотреть на время, то кажется что функция "недосыпает" отведенное время. Причем delayer без проблем работает с простой функцией, а начинаешь добавлять логику, таски - появляются задержки, отсюда недосып функции.
  • Вопрос задан
  • 327 просмотров
Подписаться 2 Простой 20 комментариев
Решения вопроса 1
@to_east Автор вопроса
Оказывается необходимо произвести еще одну задержку, после итерации, чтобы быть увереным, что расчет задержки будет в следующем интервале времени
Код:
import asyncio
import time
from datetime import datetime
from dataclasses import dataclass


@dataclass
class State:
    value: int


async def delayer(interval: float):
    while True:
        now = time.time()
        delay = interval - (now % interval)
        yield await asyncio.sleep(delay)
        await asyncio.sleep(interval / 3)


async def updater(state: State, interval: float):
    async for _ in delayer(interval):
        state.value += 1
        print(f"{datetime.now()} updater {state.value}")


async def main():
    state = State(0)
    update_interval = .2

    asyncio.create_task(updater(state, update_interval))

    while True:
        print(f"{datetime.now()} main {state.value}")
        await asyncio.sleep(.1)

if __name__ == "__main__":
    asyncio.run(main())

Вывод:
2025-05-16 03:14:49.783501 main 0
2025-05-16 03:14:49.801776 updater 1
2025-05-16 03:14:49.886596 main 1
2025-05-16 03:14:49.988087 main 1
2025-05-16 03:14:50.001721 updater 2
2025-05-16 03:14:50.090751 main 2
2025-05-16 03:14:50.191829 main 2
2025-05-16 03:14:50.200738 updater 3
2025-05-16 03:14:50.293298 main 3
2025-05-16 03:14:50.394093 main 3
2025-05-16 03:14:50.401138 updater 4
2025-05-16 03:14:50.495234 main 4
2025-05-16 03:14:50.595979 main 4
2025-05-16 03:14:50.601680 updater 5
2025-05-16 03:14:50.696821 main 5
2025-05-16 03:14:50.797915 main 5
2025-05-16 03:14:50.800797 updater 6
2025-05-16 03:14:50.899381 main 6
2025-05-16 03:14:51.000355 main 6
2025-05-16 03:14:51.000852 updater 7
2025-05-16 03:14:51.101351 main 7
2025-05-16 03:14:51.201208 updater 8
2025-05-16 03:14:51.201701 main 8
2025-05-16 03:14:51.302583 main 8
2025-05-16 03:14:51.401710 updater 9
2025-05-16 03:14:51.403641 main 9
2025-05-16 03:14:51.505192 main 9
2025-05-16 03:14:51.601012 updater 10
2025-05-16 03:14:51.606743 main 10
2025-05-16 03:14:51.707484 main 10
2025-05-16 03:14:51.801544 updater 11
2025-05-16 03:14:51.808309 main 11
2025-05-16 03:14:51.909809 main 11
2025-05-16 03:14:52.000806 updater 12
2025-05-16 03:14:52.011606 main 12
2025-05-16 03:14:52.113415 main 12
2025-05-16 03:14:52.202388 updater 13
2025-05-16 03:14:52.214291 main 13
2025-05-16 03:14:52.315195 main 13
2025-05-16 03:14:52.401254 updater 14
2025-05-16 03:14:52.416091 main 14
2025-05-16 03:14:52.517344 main 14
2025-05-16 03:14:52.601120 updater 15
2025-05-16 03:14:52.618772 main 15
2025-05-16 03:14:52.720507 main 15
2025-05-16 03:14:52.801609 updater 16
2025-05-16 03:14:52.821462 main 16
2025-05-16 03:14:52.923387 main 16
2025-05-16 03:14:53.001534 updater 17
2025-05-16 03:14:53.024396 main 17
2025-05-16 03:14:53.126190 main 17
2025-05-16 03:14:53.201231 updater 18
2025-05-16 03:14:53.227011 main 18
2025-05-16 03:14:53.329709 main 18
2025-05-16 03:14:53.402908 updater 19
2025-05-16 03:14:53.430889 main 19
2025-05-16 03:14:53.532470 main 19
2025-05-16 03:14:53.601380 updater 20
2025-05-16 03:14:53.633968 main 20
2025-05-16 03:14:53.735617 main 20
2025-05-16 03:14:53.801714 updater 21
2025-05-16 03:14:53.836654 main 21
2025-05-16 03:14:53.938479 main 21
2025-05-16 03:14:54.001544 updater 22
2025-05-16 03:14:54.039524 main 22
2025-05-16 03:14:54.140508 main 22
2025-05-16 03:14:54.201517 updater 23
2025-05-16 03:14:54.241385 main 23

Вот где "main 7" в строчке, главный поток немного опережает выполнение updater-a, но для моей задачи это приемлемо, главное что updater не вызывается два раза подряд.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
fenrir1121
@fenrir1121
Начни с документации
Кто попадет на эту страницу: проблема кода в сомнительной логике расчета задержки, которая может быть околонулевой.
Не пишите свои велосипеды, если нужны периодические асинхронные задачи используйте Sheduling tasks в taskiq
Ответ написан
Комментировать
HemulGM
@HemulGM Куратор тега Python
Delphi Developer, сис. админ
Если тебе нужно точное время выполнения, тебе нужно привязываться ко времени, а не к Sleep и await.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
Greenway Global Новосибирск
от 150 000 ₽
SPA2099 Москва
До 100 000 ₽
HR Prime Москва
от 300 000 до 3 800 000 ₽