Arhn - архитектура программирования

Самый безопасный способ реализовать несколько таймеров внутри потока в C++

Как следует из названия, я ищу лучший способ реализовать несколько таймеров на С++ (а не на С++ 11). Моя идея состоит в том, чтобы иметь один pthread (posix) для обработки таймеров. Мне нужно как минимум 4 таймера, 3 периодических и 1 одиночный выстрел. Минимальное разрешение должно быть 1 секунда (для самого короткого таймера) и 15 часов для самого длинного. Все таймеры должны работать одновременно.

Это разные реализации, которые приходят мне на ум (я не знаю, являются ли они самыми безопасными в среде потоков или самыми простыми):

1) Используя структуру itimerspec, sigaction и sigevent следующим образом:

static int Tcreate( char *name, timer_t *timerID, int expireMS, int intervalMS )
{
   struct sigevent         te;
   struct itimerspec       its;
   struct sigaction        sa;
   int                     sigNo = SIGRTMIN;

   sa.sa_flags = SA_SIGINFO;
   sa.sa_sigaction = app;
   sigemptyset(&sa.sa_mask);
   if (sigaction(sigNo, &sa, NULL) == -1)
   {
       perror("sigaction");
   }

   /* Set and enable alarm */
   te.sigev_notify = SIGEV_SIGNAL;
   te.sigev_signo = sigNo;
   te.sigev_value.sival_ptr = timerID;
   timer_create(CLOCK_REALTIME, &te, timerID);

   its.it_interval.tv_sec = 0;
   its.it_interval.tv_nsec = intervalMS * 1000000;
   its.it_value.tv_sec = 0;
   its.it_value.tv_nsec = expireMS * 1000000;
   timer_settime(*timerID, 0, &its, NULL);

   return 1;
}

2) Использование clock() и проверка разницы во времени, например:

std::clock_t start;
double duration;
start = std::clock();
duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;

3) Используя хроно, как это:

auto diff = tp - chrono::system_clock::time_point();
  cout << "diff:" << chrono::duration_cast<chrono::minutes>(diff).count()
       << " minute(s)" << endl;
  Days days = chrono::duration_cast<Days>(diff);
  cout << "diff:" << days.count() << " day(s)" << endl;

Пожалуйста, рассматривайте это как идеи, а не реальный рабочий код.

Каково ваше мнение об этом?

12.08.2017

  • Вариант № 3 — С++ 11. 13.08.2017
  • Спасибо за голову. Я должен быть С++ 11, прямо сейчас проверил на справочном сайте С++. Если бы вы могли предложить другие методы, это очень помогло бы мне. Мне не нужен, конечно, полный код, просто идея реализации. 13.08.2017
  • Вы можете рассчитать следующую задержку истечения срока действия и использовать системные вызовы select/poll/epoll (используя тайм-аут). Или сон/часы_наносон. В любом случае вам нужно будет проверять время при пробуждении, так как оно может вернуться раньше. Хорошая вещь с методом select/poll/epoll заключается в том, что он хорошо интегрируется в цикл событий, вам даже не нужен отдельный поток. 13.08.2017

Ответы:


1

Если ваш поток таймера отвечает только за таймеры, а минимальное разрешение составляет 1 секунду, и время не должно быть таким точным (т. е. если +/- 0,1 секунды достаточно), тогда простая реализация для потока таймера состоит в том, чтобы просто заснуть на 1 секунду, проверить наличие таймеров, которые нужно запустить, и повторить, как в следующем псевдокоде:

repeat:
  sleep 1
  t = t+1
  for timer in timers where timer(t) = true:
    fire(timer)

Трудной частью будет заполнение структуры, в которой хранятся таймеры — предположительно, таймеры будут устанавливаться другими потоками, возможно, несколькими потоками, которые могут пытаться устанавливать таймеры одновременно. Было бы целесообразно использовать некоторую стандартную структуру данных, такую ​​как потокобезопасная очередь, для передачи сообщений в поток таймера, который в каждом цикле затем обновлял бы сам набор таймеров:

repeat:
  sleep 1
  t = t+1
  while new_timer_spec = pop(timer_queue):
    add_timer(new_timer_spec)
  for timer in timers where timer(t) = true:
    fire(timer)

Еще одна вещь, которую следует учитывать, — это природа fire(timer) — то, что здесь нужно делать, действительно зависит от потребностей потоков, использующих таймеры. Возможно, было бы достаточно просто установить переменную, которую они могли бы прочитать, или, может быть, это могло бы запустить сигнал, который потоки могли бы прослушивать.

13.08.2017
  • Я буду использовать обработчик событий как для установки таймера, так и для получения огня (таймера), поэтому у меня не будет нескольких потоков, пытающихся установить его одновременно. Большое спасибо за вашу идею! 13.08.2017
  • на самом деле, при таком подходе вам не нужен таймер, вы можете просто поддерживать счетчик в качестве таймера и уменьшать этот счетчик каждый раз, когда поток просыпается. Если счетчик равен 0, то время таймера истекло. 02.05.2018

  • 2

    Поскольку все ваши таймеры, по-видимому, создаются через единый API (т. е. управляющий код видит все таймеры), вы можете полностью избежать сигналов или циклов занятости и вести отсортированный список таймеров (например, std::map с ключом по крайнему сроку) и просто подождите переменную условия, используя (например) pthread_cond_timedwait. Мьютекс переменной условия защищает список таймеров.

    Если вы запланируете новый таймер, крайний срок которого раньше, чем текущий «следующий» таймер, вам нужно будет разбудить спящий поток и запланировать скорректированный сон (если бы это не было для этого требования, вы могли бы использовать простой usleep или что-то еще). Все это происходит внутри мьютекса, связанного с условной переменной.

    Вам не нужно использовать условные переменные, но они кажутся самыми чистыми, поскольку связанный с ними мьютекс естественным образом используется для защиты списка таймеров. Вероятно, вы могли бы также построить это поверх семафона с sem_timedwait, но или поверх select на внутреннем сокете, канале или что-то в этом роде, но тогда вы застряли отдельно, контролируя многопоточный доступ к очереди таймера.

    13.08.2017
  • Вы имеете в виду что-то вроде этого? clock_gettime(CLOCK_REALTIME, &now) reltime = sleep_til_this_absolute_time -now; cond_relative_timed_wait(c, m, &reltime); Но во время ожидания поток будет удерживать мьютекс, верно? Что тогда произойдет с другими потоками, ожидающими его получения? Возможно, я неправильно понял, как работает pthread_cond_timedwait. Не могли бы вы подтвердить, пожалуйста? 13.08.2017
  • @Podarce - условные переменные работают так, что мьютекс освобождается, когда вы wait() на них. В частности, вы должны использовать мьютекс для защиты структуры, содержащей отсортированный список таймеров. Любой, кто добавляет таймер, возьмет мьютекс, добавит таймер и проверит, нужно ли скорректировать время ожидания, а затем оставит мьютекс. 13.08.2017
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..