2016-08-10 2 views
1

В предисловии, я нахожусь в системе Unix (linux), используя gcc.Код запуска для x времени

Я застрял в том, как точно реализовать способ запуска части кода в течение определенного времени.

Вот пример того, что я работаю с:

struct timeb start, check; 
int64_t duration = 10000; 
int64_t elapsed = 0; 

ftime(&start); 

while (elapsed < duration) { 
    // do a set of tasks 
    ftime(&check); 
    elapsed += ((check.time - start.time) * 1000) + (check.millitm - start.millitm); 
} 

Я думал, это было бы вели к 10000ms или 10 секунд, но этого не произошло, почти мгновенно. Я основывал это на других вопросах, таких как How to get the time elapsed in C in milliseconds? (Windows). Но потом я подумал, что если при первом вызове ftime структура будет time = 1, millitm = 999, а при втором вызове time = 2, millitm = 01 это будет вычисление прошедшего времени как 1002 миллисекунды. Есть что-то, чего я не хватает?

Также предложения в различных вопросах stackoverflow, ftime() и gettimeofday() перечислены как устаревшие или устаревшие.

Я считаю, что могу преобразовать время начала в миллисекунды, а время проверки - в millseconds, а затем вычесть начало из проверки. Но миллисекунды с эпохи требуют 42 бит, и я стараюсь держать все в цикле максимально эффективным.

Какой подход я могу принять к этому?

+4

Вы можете организовать отправку сигнала в ваш процесс по истечении определенного времени через 'timer_create()'. – EOF

+3

Я бы ожидал 'elapsed = ...', а не' elapsed + = ..' – chux

+0

@chux ok yes Я не хотел включать это. Моя проблема связана с неправильным определением изменения во времени с помощью этого примера. –

ответ

3

Код неверный расчетное истекшее время.

// elapsed += ((check.time - start.time) * 1000) + (check.millitm - start.millitm); 
elapsed = ((check.time - start.time) * (int64_t)1000) + (check.millitm - start.millitm); 

Существует некоторая озабоченность по поводу check.millitm - start.millitm. В системах с struct timeb *tp можно ожидать, что millitm будет повышаться до int до вычитания. Таким образом, разница будет в диапазоне [-1000 ... 1000].

 struct timeb { 
      time_t   time; 
      unsigned short millitm; 
      short   timezone; 
      short   dstflag; 
     }; 

ИМО, более надежный код будет обрабатывать преобразование мс в отдельную вспомогательную функцию. Это соответствует OP «Я полагаю, что смогу преобразовать время начала в миллисекунды и время проверки в millseconds, а затем вычесть начало из проверки».

int64_t timeb_to_ms(struct timeb *t) { 
    return (int64_t)t->time * 1000 + t->millitm; 
} 

struct timeb start, check; 
ftime(&start); 
int64_t start_ms = timeb_to_ms(&start); 

int64_t duration = 10000 /* ms */; 
int64_t elapsed = 0; 

while (elapsed < duration) { 
    // do a set of tasks 
    struct timeb check; 
    ftime(&check); 
    elapsed = timeb_to_ms(&check) - start_ms; 
} 
+0

Но как бы это устранить проблему, упомянутую в «Но потом я подумал, что если по первому вызову ftime структура равна времени = 1, millitm = 999, а во втором времени вызова = 2, millitm = 01, будет вычисляться прошедшее время как 1002 миллисекунды ». –

+0

@Austin Bodzas 'millitm' - это' 'непродолжительный короткий', который продвигается на' int' на вашей платформе. -999' -998. '(2-1) * 1000 + -998' равно 2. Если это касается того, что' unsigned short' может быть того же диапазона, что и 'unsigned', используйте' ((check.time - start.time) * (int64_t) 1000) + ((int) check.millitm - (int) start.millitm); ' – chux

0

Если вам нужна эффективность, пусть система отправит вам сигнал по истечении таймера.

Традиционно вы можете установить таймер с разрешением в секундах с помощью системного вызова alarm(2).

После этого таймер отправляет вам SIGALRM. По умолчанию этот сигнал отключается.

Если вы обрабатываете сигнал, вы можете longjmp(2) от проводника в другое место.

Я не думаю, что он становится намного более эффективным, чем SIGALRM + longjmp (с асинхронным таймером, ваш код в основном работает без проблем, не выполняя никаких дополнительных проверок или вызовов).

Ниже приведен пример для вас:

#define _XOPEN_SOURCE 
#include <unistd.h> 
#include <stdio.h> 
#include <signal.h> 
#include <setjmp.h> 

static jmp_buf jmpbuf; 

void hndlr(); 
void loop(); 
int main(){ 

    /*sisv_signal handlers get reset after a signal is caught and handled*/ 
    if(SIG_ERR==sysv_signal(SIGALRM,hndlr)){ 
     perror("couldn't set SIGALRM handler"); 
     return 1; 
    } 

    /*the handler will jump you back here*/ 
    setjmp(jmpbuf); 

    if(0>alarm(3/*seconds*/)){ 
     perror("couldn't set alarm"); 
     return 1; 
    } 

    loop(); 

    return 0; 
} 

void hndlr(){ 
    puts("Caught SIGALRM"); 
    puts("RESET"); 
    longjmp(jmpbuf,1); 
} 

void loop(){ 
    int i; 
    for(i=0; ; i++){ 
     //print each 100-milionth iteration 
     if(0==i%100000000){ 
      printf("%d\n", i); 
     } 
    } 
} 

Если alarm(2) не достаточно, вы можете использовать в качестве timer_create(2)EOF предлагает.

+0

' man getcontext' * Не оставляйте обработчик с помощью longjmp (3): не определено, что произойдет с контекстами. Вместо этого используйте siglongjmp (3) или setcontext(). * – EOF

+0

@EOF manpage говорит, что siglongjmp работает как longjmp, но также восстанавливает сигнальную маску. Я не устанавливаю сигнальную маску в этом примере, поэтому, я думаю, это не имеет значения. Вообще, я думаю, я бы использовал sigaction и siglongjmp, но я думаю, что longjmp здесь ОК. – PSkocik

+0

Проблема в том, что 'setjmp/longjmp' были явно недоказаны в C (например,' signal' и другие функции). В результате различные реализации делают совершенно разные вещи для этих функций, а C не гарантирует многого. Учитывая, что 'longjmp()' не входит в список асинхронных функций (как 'puts()'), ваша программа обнаруживает * неопределенное поведение *. – EOF

Смежные вопросы