erniesto77
@erniesto77
oop, rb, py, php, js

Цикл с таймаутом ровно в 1 секунду?

Привет всем.
Суть вопроса - как можно добиться того, что бы каждая итерация цикла длилась ровно 1 секунду? Цель: синхронизировать выполнение функции (допустим check_storage), с реальным временем с точностью до секунды для мониторинга данных (например из redis) на предмет их изменений. Коротко скажу, что выполнять проверку каждую секунду обязательно, потому что некоторые изменения в хранилище инициируются извне.
Как пробую я. По крону каждую минуту:
*/1 * * * * php /var/www/test/timer.php
в цикле от 1 до 60 выполняю свой check_storage и после каждой итерации вызываю паузу sleep в 1 секунду:
for ($i = 1; $i <= 60; $i++)  {
    check_storage(); // выполняется примерно 0.03 секунды
    sleep(1); // пауза 1 секунда
}

но check_storage тоже занимает какое то время и общая пауза на каждой итерации длится 1 секунду + время выполнения check_storage, получается каждый цикл продолжается еще 2 итерации в то время когда пошла новая минута с новым циклом.
Нужно как то уместить 60 итераций в одну минуту или найти другой способ. Уверен что для этого есть какие то проверенные временем методы, но я пока с таким не сталкивался и поэтому прошу знатоков помочь прийти к правильному решению. Заранее благодарю за любую помощь!
  • Вопрос задан
  • 1759 просмотров
Решения вопроса 3
pi314
@pi314
Президент Солнечной системы и окрестностей
Ваш вопрос можно приводить в качестве наглядного пособия на тему "Как именно вылазит боком кривая архитектура системы" :) Основная проблема в том, что интеграция компонент через storage есть зло, рано или поздно (чаще - рано) заставляющее разработчика выполнять стойку на ушах. Так что, если есть такая возможность, постарайтесь устранить зло в корне, т.е. найти способ узнавать об изменениях, ну, или хотя бы о самом факте, до того, а не после. Тогда не придется ничего опрашивать в цикле, а только реагировать на изменения. А это уже - половина проблемы!

Если возможности нет, а делать все равно надо, сначала смитритесь с тем, что Вы никогда на PHP не добьетесь ровненько 86400 вызовов в сутки каждую секунду, если только не поставите ядро реального времени / не напишете соответствующий код на С и т.д. и т.п. Особенно, если storage крутится на том же процессоре в той же ОС, и количесво данных в нем будет со временем увеличиваться. Но это почти наверняка и не нужно, а нужно проанализировать задачу и понять, что на самом деле критично и какие отклонения от идеала возможны без ущерба для функциональности.

"Примерно 0.03с" само по себе еще не о чем не говорит. Это всегда или в случае, если изменений нет, или если они небольшого объема? А если 90% данных обновились? Это - раз. Два: если изменения обнаружены, сколько может занять их обработка (в худшем случае)?.. И, наконец, три: если уже наступила "следующая секунда", а мы все еще не закончили обработку прошлых изменений, возникает целый ряд вопросов. Имеет ли в этом случае вообще смысл проверять новые изменения (сможем ли мы их осмысленно обработать, если найдем)? Если да, понадобится как минимум 2 потока. Если нет, насколько критично пропустить эту секунду? А сколько еще можно пропустить без ущерба для функционала? Предположим, это не критично, и мы уже пропустили секунду (или несколько), что нам важнее: чтоб следующая проверка выполнилась как можно ближе к границе "следующей секунды", или как можно быстрее? И т.д. и т.п.

Не зная ответов на эти и подобные вопросы, невозможно предложить "правильное" решение. Но в качестве сферического коня в вакууме можно посоветовать бесконечный цикл, в котором выполняется проверка, реакция на найденные изменения, после чего вычисляется время до следующей проверки, на которое выполняется sleep().
Ответ написан
Комментировать
RiseOfDeath
@RiseOfDeath
Диванный эксперт.
Точный промежуток времени вам даст только захват системного таймера, но это вам уже модуль ядерный делать надо.
Ответ написан
Комментировать
sergiks
@sergiks Куратор тега PHP
♬♬
Redis имеет механизм уведомлений об операциях. См. notifications.

Что касается таймера на php, можете определить себе допустимую ошибку, напр. в 20ms и спать не секунду, а микросекунды, используя ф-ю usleep(2E4), и проснувшись, проверять системное время с точностью до микросекунд, опять же – через microtime(TRUE). Если до очередной секунды осталось меньше погрешности – выполнить код. И снова спать, часто просыпаясь : )

Disclaimer. Этот метод никак не гарантирует, что не проспите очередную секунду – если, например, система сильна загружена.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы