Программа неожиданно останавливается, когда я использую 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 вместе, и предотвращает отрицательное переполнение в unsigneds.

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);
Другие вопросы по тегам