Программа неожиданно останавливается, когда я использую gettimeofday() в бесконечном цикле
Я написал код, чтобы каждый цикл цикла while(1) занимал определенное время (в этом примере 10000 мкс, что равно 0,01 секунды). Проблема в том, что этот код работает довольно хорошо при запуске, но почему-то останавливается менее чем через минуту. Как будто есть ограничение на доступ к Linux времени. Сейчас я инициализирую логическую переменную, чтобы вычисление времени выполнялось один раз, а не бесконечно. Поскольку производительность меняется со временем, было бы хорошо рассчитать время вычисления для каждого цикла. Есть ли другой способ сделать это?
void some_function(){
struct timeval tstart,tend;
while (1){
gettimeofday (&tstart, NULL);
...
Some computation
...
gettimeofday (&tend, NULL);
diff = (tend.tv_sec - tstart.tv_sec)*1000000L+(tend.tv_usec - tstart.tv_usec);
usleep(10000-diff);
}
}
2 ответа
Ну, вычисления, которые вы делаете, чтобы получить разницу, неверны:
diff = (tend.tv_sec - tstart.tv_sec)*1000000L+(tend.tv_usec - tstart.tv_usec);
Вы смешиваете разные целочисленные типы. tv_usec
может быть unsigned
количество, которое вы вычитаете из другого unsigned
и может переполниться.... после этого вы получите в результате целую секунду плюс количество, которое составляет около 4.0E09usec
, Это примерно 4000сек. или больше часа.... примерно. Лучше проверить, есть ли перенос, и в этом случае увеличить tv_sec
, а затем вычесть 10000000
от tv_usec
чтобы получить правильное положительное значение.
Я не знаю реализацию, которую вы используете для struct timeval
но наиболее вероятным является то, что tv_sec
это time_t
(это может быть даже 64 бит) в то время как tv_usec
обычно это просто unsigned
32-битное значение, так как дальше идти от этого не собирается 1000000
,
Позвольте мне проиллюстрировать... предположим, что вы прошли 100 мс, выполняя вычисления.... и это происходит в середине секунды.... у вас есть
tstart.tv_sec = 123456789; tstart.tv_usec = 123456;
tend.tv_sec = 123456789; tend.tv_usec = 223456;
когда вы вычитаете, это приводит к:
tv_sec = 0; tv_usec = 100000;
но давайте предположим, что вы выполнили свои вычисления в то время как второе изменение
tstart.tv_sec = 123456789; tstart.tv_usec = 923456;
tend.tv_sec = 123456790; tend.tv_usec = 23456;
разница во времени снова равна 100 мсек, но теперь, когда вы вычисляете выражение, вы получаете для первой части 1000000 (одна полная секунда), но после вычитания второй части вы получаете 23456 - 923456 =*=> 4294067296
(*) с переполнением. так что вы получите usleep(4295067296)
или же 4295s.
или же 1h 11m
Больше. Я думаю, что у вас не хватило терпения дождаться его завершения... но это может происходить с вашей программой в зависимости от того, как struct timeval
определено.
Правильный способ выполнения переноса на работу - это изменить порядок суммирования, чтобы сначала выполнить все добавления, а затем вычитания. Это заставляет приводить к целым числам со знаком при работе с signed
а также unsigned
вместе, и предотвращает отрицательное переполнение в unsigned
s.
diff = (tend.tv_sec - tstart.tv_sec) * 1000000 + tstart.tv_usec - tend.tv_usec;
который анализируется как
diff = (((tend.tv_sec - tstart.tv_sec) * 1000000) + tstart.tv_usec) - tend.tv_usec;
с man-страницы usleep
#include <unistd.h>
int usleep(useconds_t usec);
usec - это unsigned int, теперь угадайте, что происходит, когда diff> 10000 в нижней строке
usleep(10000-diff);