LINUX.ORG.RU

Как лучше сделать низкоточные таймеры?

 ,


0

2

Пишу софт на C.

Нужно выполнять ряд действий за равные промежутки времени (высокая точно НЕ нужна). Как правильно узнать что текущий промежуток уже закончился?

Варианты:

1.

action();

clock_gettime(CLOCK_MONOTONIC, &point)

if (compare(point, end)) break;

Тут для меня не очень понятно насколько вообще будет разумно использовать так ресурсы: clock_gettime() не бесплатен

2. вводить тред в котором внутри будет:
thread2() {
    sleep(100);
    global_var = true;
}

затем после каждого action(); делать
if (global_var) break;


3. ДЭБИЛЬНЫЙ, но работающий вариант с сигналами
void handler(int signo) {
    signal(SIGALRM, handler);
}

...

{
	struct itimerval tval = {.it_value.tv_sec = 1};
	setitimer(ITIMER_REAL, &tval, NULL);
}

Мне крайне странно, что после каждого срабатывания хендлера, он сбрасывается, и надо каждый раз делать signal(SIGALRM, handler); Что за бред?



Какие еще есть варианты?

★★★★★

Ответ на: комментарий от COKPOWEHEU

Да, я могу настроить чтобы иметь периоды и вручную не заводить таймер каждый раз, меня просто удручает ситуация что каждый раз сбрасывается signal handler и его нужно снова устанавливать. Мне кажется это неэффективным и нелогичным. Может есть с сигналами какое-то более адекватное решение?

reprimand ★★★★★
() автор топика
Ответ на: комментарий от reprimand
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>

void tmr(int sig){
  printf("Time!\n");
}

int main(){
  signal(SIGALRM, tmr);
  static struct itimerval tval = {.it_value.tv_sec = 1, .it_interval.tv_sec = 1};
  setitimer(ITIMER_REAL, &tval, NULL);
  
  for(int i=0; i<10; i++){
    usleep(10000000);
  }
}

Да вроде работает без переустановок

COKPOWEHEU
()

А можно и по методу конечного автомата:

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

unsigned long get_time_ms(){
  struct timeval tv;
  unsigned long time_ms;
  gettimeofday(&tv,NULL);
  time_ms = tv.tv_usec/1000 + tv.tv_sec*1000;
  return time_ms;
}

void task_func(unsigned long cur_time, unsigned long interval){
  static unsigned long next_time = 0;
  if( cur_time >= next_time ){
    next_time = cur_time + interval;
    printf("Time!\n");
  }
}

int main(){
  for(int i=0; i<100; i++){
    usleep(100000);
    task_func( get_time_ms(), 1000 );
  }
}
COKPOWEHEU
()
Последнее исправление: COKPOWEHEU (всего исправлений: 1)
Ответ на: комментарий от reprimand
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    struct timespec t, t_now;

    clock_gettime(CLOCK_MONOTONIC, &t);
    t.tv_sec += 1;
    t.tv_nsec = 0;

    for (int i = 0; i < 10; ++i) {
        while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL) != 0) {
            if (errno == EINTR) continue;
            perror(NULL);
            return -1;
        }
        clock_gettime(CLOCK_MONOTONIC, &t_now);
        printf("%d %d.%09d\n", i, (int)t_now.tv_sec, (int)t_now.tv_nsec);
        t.tv_sec += 1;
    }

    return 0;
}

Если я правильно понял задачу.

AlexVR ★★★★★
()

Если у тебя хотя бы 2.6.25, то можно взять вот такую штуку:

https://man7.org/linux/man-pages/man2/timerfd_create.2.html

Самое сочное в ней то, что можно параллельно ждать таймер и делать что-то в фоне. И также асинхронно получить сразу несколько срабатываний таймера, если программа по каким-то причинам была занята.

PS: на самом деле я просто ненавижу сигналы, потому что это нагромождение костылей, которое помимо того, что сложное в использовании, так еще и обладает неизбежными ограничениями, которые еще больше усложняют использование. И в итоге от кодера требуется мастерство хождения на двух уровнях костылей.

byko3y ★★★★
()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от byko3y

Для сигналов, кстати, есть signalfd, чтобы их тоже в эвентлупе обрабатывать.

anonymous
()

В потоке обработки таймера:

повторять цикл:
    задача := задачник.найти_ближайшую_задачу()
    сейчас := получить_текущее_время()
    если задача не пустая:
        осталось_спать := задача.назначенное_время - сейчас
    иначе:
        осталось_спать := 1000

    если осталось_спать <= 0:
        задачник.выбросить(задача)
        задача.выполнить()
        перейти на начало цикла

    событие := канал.ожидать(сколько_ждать=осталось_спать)
    если событие.пора_заканчивать_работу?():
        выйти из цикла
поток.завершить()

задачник и канал должны быть доступны из других потоков. Добавление новой задачи будет примерно так:

задачник.добавить(новая_задача)
канал.послать(событие=уведомление_о_новой_задаче)

При завершении нужно послать уведомление о завершении:

канал.послать(событие=уведомление_о_завершении)

Задачник скорее всего будет деревом, обёрнутым в блокировку. Канал — pipe, socketpair или условная переменная. Зависит от того, что ещё будет ожидать поток для задач по таймеру. Если ты там уже какие-то файловые дескрипторы слушаешь, то имеет смысл использовать pipe или socketpair. Если потоков обработки задач несколько, то лучше подойдёт условная переменная. В этом случае ещё понадобится не просто искать следующую задачу, а ещё и выдёргивать перед ожиданием и перевставлять обратно в задачник, если дело не дошло до выполнения задачи.

i-rinat ★★★★★
()

Как правильно узнать что текущий промежуток уже закончился?

(псевдокодом): time_now-time_prev>=period+delta… нету другого/иного способа чем помнить прежний момент, отсекать нынешний и считать их разницу.. всё прочее - это просто попытки «подсобить системе» и считать указанное пореже, причем с возможностью отстрелить пятку.

MKuznetsov ★★★★★
()

clock_gettime() не бесплатен

А он не через vdso работает? Если через него, то там же просто чтение из памяти без сисколов.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

clock_gettime() не бесплатен

А он не через vdso работает? Если через него, то там же просто чтение из памяти без сисколов

Через VDSO работает только COARSE_чотатам. Всё остальное лезет в ядро.

byko3y ★★★★
()

Какие еще есть варианты?

man crontab

anonymous
()
Ответ на: комментарий от Eddy_Em

Ну то есть по сути это clock_gettime.

----

А зачем ты использовал число с плавающей запятой для этих целей?

reprimand ★★★★★
() автор топика
Ответ на: комментарий от byko3y

Можешь подробнее рассказать что за COARSE? Загуглил vdso coarse и получил какой-то не очень читабельный результат.

reprimand ★★★★★
() автор топика
Ответ на: комментарий от beastie

Ну, может и нет.
Проблема в том, что:

1. Он выбрасывает из системных вызовов, и каждый раз нужно убеждаться что никто внутри не использует их либо правильно с ними работает
2. Нужно переустанавливать хендлер. Это странно.

reprimand ★★★★★
() автор топика
Ответ на: комментарий от reprimand

https://github.com/btorpey/clocks

ClockBench.cpp
                   Method       samples     min     max     avg  median   stdev
           CLOCK_REALTIME       1023      15.00 16182.00          33.35 8098.50  505.15
    CLOCK_REALTIME_COARSE       1023       0.00    0.00    0.00    0.00    0.00
          CLOCK_MONOTONIC       1023      16.00  244.00   19.19  130.00   12.26
      CLOCK_MONOTONIC_RAW       1023      15.00   18.00   16.70   16.50    0.58
   CLOCK_MONOTONIC_COARSE       1023       0.00    0.00    0.00    0.00    0.00
              cpuid+rdtsc       1023      81.00   91.00   86.30   86.00    1.33
                   rdtscp       1023      25.00   31.00   27.61   28.00    1.22
                    rdtsc       1023      15.00   21.00   19.06   18.00    1.04

drsm ★★
()
Ответ на: комментарий от anonymous

не, там разные единицы измерения между CLOCK и asm вариантами.

drsm ★★
()

на вендовой msvcrt если кому интересно

GetTickCount();                           // 1111200
GetTickCount64();                         // 1333400
__builtin_ia32_rdtsc();                   // 5555600
__builtin_ia32_rdtscp(&rdt);              // 7555600
timeGetTime();                            // 8893000
QueryPerformanceCounter(&qpc);            //11333400
std::chrono::steady_clock::now();         //12889000
std::chrono::high_resolution_clock::now();//12893200
std::chrono::system_clock::now();         //17362400
timespec_get(&tms,TIME_UTC);              //25121300
anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.