Точный сон / задержка внутри цикла Python
У меня есть время True, который отправляет переменные во внешнюю функцию, а затем использует возвращаемые значения. Этот процесс отправки / получения имеет настраиваемую пользователем частоту, которая сохраняется и считывается из внешнего файла конфигурации.ini.
Я пробовал time.sleep(1 / Frequency), но не удовлетворен точностью, учитывая количество потоков, используемых в других местах. Например, частота 60 Гц (период 0,0166667) дает "фактический" период time.sleep() ~0,0311.
Я предпочел бы использовать дополнительный цикл while, который сравнивает текущее время с временем начала плюс период следующим образом:
EndTime = time.time() + (1 / Frequency)
while time.time() - EndTime < 0:
sleep(0)
Это подошло бы к концу моей функции True следующим образом:
while True:
A = random.randint(0, 5)
B = random.randint(0, 10)
C = random.randint(0, 20)
Values = ExternalFunction.main(Variable_A = A, Variable_B = B, Variable_C = C)
Return_A = Values['A_Out']
Return_B = Values['B_Out']
Return_C = Values['C_Out']
#Updated other functions with Return_A, Return_B and Return_C
EndTime = time.time() + (1 / Frequency)
while time.time() - EndTime < 0:
time.sleep(0)
Я что-то упустил, так как добавление цикла while заставляет функцию выполняться только один раз. Как я могу заставить вышеописанное функционировать правильно? Является ли это лучшим подходом к "точному" управлению частотой в операционной системе не в реальном времени? Должен ли я использовать потоки для этого конкретного компонента? Я тестирую эту функцию на Windows 7 (64-разрядная версия) и Ubuntu (64-разрядная версия).
3 ответа
Если я правильно понял ваш вопрос, вы хотите выполнить ExternalFunction.main
на заданной частоте. Проблема в том, что исполнение ExternalFunction.main
само по себе занимает некоторое время. Если вам не нужна очень точная точность - кажется, вам это не нужно - я предлагаю сделать что-то подобное.
import time
frequency = 1 # Hz
period = 1.0/frequency
while True:
time_before = time.time()
[...]
ExternalFunction.main([...])
[...]
while (time.time() - time_before) < period:
time.sleep(0.001) # precision here
Вы можете настроить точность под свои нужды. Большая точность (меньшее число) заставит внутренний цикл while выполняться чаще.
Это дает достойные результаты, когда не используются потоки. Однако при использовании потоков Python GIL (глобальная блокировка интерпретатора) обеспечивает одновременную работу только одного потока. Если у вас огромное количество потоков, возможно, программе потребуется слишком много времени, чтобы программа вернулась к вашему основному потоку. Увеличение частоты изменений Python между потоками может дать вам более точные задержки.
Добавьте это в начало вашего кода, чтобы увеличить частоту переключения потоков.
import sys
sys.setcheckinterval(1)
1
это число инструкций, выполненных в каждом потоке перед переключением (по умолчанию 100), большее число повышает производительность, но увеличивает время переключения потоков.
Вы можете попробовать Python-паузу
Пауза до времени Unix с точностью до миллисекунды:
import pause pause.until(1370640569.7747359)
Пауза с использованием datetime:
import pause, datetime dt = datetime.datetime(2013, 6, 2, 14, 36, 34, 383752) pause.until(dt)
Вы можете использовать это как:
freqHz=60.0
td=datetime.timedelta(seconds=1/freqHz)
dt=datetime.now()
while true:
#Your code here
dt+=td
pause.until(dt)
Другое решение для точной задержки - использовать функцию perf_counter() из времени модуля. Особенно полезно в окнах, так как time.sleep не является точным в миллисекундах. Ниже приведен пример, где функция correct_delay создает задержку в миллисекундах.
import time
def accurate_delay(delay):
''' Function to provide accurate time delay in millisecond
'''
_ = time.perf_counter() + delay/1000
while time.perf_counter() < _:
pass
delay = 10
t_start = time.perf_counter()
print('Wait for {:.0f} ms. Start: {:.5f}'.format(delay, t_start))
accurate_delay(delay)
t_end = time.perf_counter()
print('End time: {:.5f}. Delay is {:.5f} ms'.
format(t_end, 1000*(t_end - t_start)))
sum = 0
ntests = 1000
for _ in range(ntests):
t_start = time.perf_counter()
accurate_delay(delay)
t_end = time.perf_counter()
print('Test completed: {:.2f}%'.format(_/ntests * 100), end='\r', flush=True)
sum = sum + 1000*(t_end - t_start) - delay
print('Average difference in time delay is {:.5f} ms.'.format(sum/ntests))`