Отслеживание тайм-аута с помощью std :: chrono :: duration

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

Функция выполняет следующее:

  1. Выполняет некоторую настройку и запускает несколько потоков с std::future и std::async.
  2. Следит за потоками, используя std::future::wait_for() в цикле. По сути, я измеряю время каждого вызова wait_for() и вычитаю время, которое потребовалось, из тайм-аута. Этот новый тайм-аут затем используется при проверке следующего потока. Моя цель здесь - убедиться, что все потоки, которые я запускаю, завершат свою работу до истечения тайм-аута (то есть параметра тайм-аута, переданного функции).

Псевдокод ниже:

void myFunctionWithTimeout(/*some other inputs*/ const long timeout_us) {
  auto start_time = std::chrono::steady_clock::now();
  double time_remaining_us = std::chrono::microseconds(timeout_us).count();

  // Launch threads here using std::future and std::async...

  auto end_time = std::chrono::steady_clock::now();
  const auto setup_time_us =
    std::chrono::duration<double, std::micro>(end_time - start_time);
  time_remaining_us -= setup_time_us.count();

  for(auto& worker : workers) {
    auto start_time = std::chrono::steady_clock::now();
    const auto status =
      worker.wait_for(std::chrono::duration<double, std::micro>(time_remaining_us));
    auto end_time = std::chrono::steady_clock::now();

    // Check status and do the appropriate actions.
    // Note that it is OK that this time isn't part of the timeout.

    const auto wait_time_us =
      std::chrono::duration<double, std::micro>(end_time - start_time);
    time_remaining_us -= wait_time_us.count();
  }
}

Мои вопросы:

  1. Есть ли более простой способ сделать то, что я предлагаю? Моя цель - сохранить оставшееся время как double, чтобы в различных вычислениях я мог учитывать доли микросекунды. Обратите внимание, что я знаю, что wait_for() точно не будет ждать указанной мной продолжительности из-за планирования и прочего, но, по крайней мере, я не хочу добавлять ошибку округления в свои вычисления.
  2. Относится к №1: нужно ли мне получать счетчик каждый раз или есть чистый способ обновить std::chrono::duration? Я хочу сохранить оставшееся время как продолжительность, а затем вычесть из него время настройки или время ожидания.
  3. Что происходит, когда time_remaining_us становится отрицательным? Как это повлияет на конструктор std::chrono::duration? Что происходит, когда std::future::wait_for() передается отрицательная продолжительность? Я не нашел этих деталей в документации, и мне интересно, хорошо ли определено здесь поведение.

================================================== =================== Отредактировано для добавления:

Согласно ответу Ховарда, я изучал возможность использования wait_until(), но я не думаю, что это сработает для меня из-за следующей проблемы, которую я обнаружил в своем исследовании (выдержка из: https://en.cppreference.com/w/cpp/thread/future/wait_until):

The clock tied to timeout_time is used, which is not required to be a monotonic clock.There are no guarantees regarding the behavior of this function if the clock is adjusted discontinuously, but the existing implementations convert timeout_time from Clock to std::chrono::system_clock and delegate to POSIX pthread_cond_timedwait so that the wait honors ajustments to the system clock, but not to the the user-provided Clock. In any case, the function also may wait for longer than until after timeout_time has been reached due to scheduling or resource contention delays.

Я читаю это так: даже если я использую steady_clock в качестве конечного времени, он будет преобразован в system_clock, а это означает, что если часы будут настроены (скажем, откат на час), у меня может получиться очень-очень большой тайм-аут. дольше, чем я ожидал.

Тем не менее, я взял концепцию вычисления времени окончания, и это упростило мой код. Вот какой-то псевдокод, где я сейчас нахожусь:

void myFunctionWithTimeout(/*some other inputs*/ const long timeout_us) {
  const auto start_time = std::chrono::steady_clock::now();
  const auto end_time =
    start_time + std::chrono::duration<double, std::micro>(timeout_us);

  // Launch threads here using std::future and std::async...

  for(auto& worker : workers) {
    const auto current_timeout_us =
      std::chrono::duration<double, std::micro>(end_time - std::chrono::steady_clock::now());
    if (current_timeout_us.count() <= 0) {  // Is this needed?
      // Handle timeout...
    }
    const auto status = worker.wait_for(current_timeout_us);

    // Check status and do the appropriate actions...
  }
}

Я все еще не уверен, смогу ли я передать отрицательную продолжительность wait_for(), поэтому сначала проверяю вручную. Если кто-нибудь знает, может ли wait_for() принять отрицательную продолжительность, дайте мне знать. Кроме того, если я неправильно понимаю документацию по wait_until(), дайте мне знать.

Да, возможно, system_clock вашего компьютера откатывается на час назад. Но это не должно происходить регулярно. Ваш system_clock отслеживает UTC, а не местное время. system_clock обычно настраивается несколько раз в день с небольшими долями секунды, поскольку ваш компьютер, вероятно, устанавливает свой system_clock с использованием протокола NTP. Даже дополнительные секунды обычно «размазываются» в течение нескольких часов, регулируя system_clock на малые доли секунды за раз.

Howard Hinnant 10.09.2018 20:16

Функции ожидания, оканчивающиеся на _for, должны немедленно возвращаться при отрицательной продолжительности.

Howard Hinnant 10.09.2018 20:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
1 672
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Просто используйте wait_until вместо wait_for. Вычислите time_point, который вы хотите подождать хотя бы один раз, и продолжайте его использовать. Если этот time_point начнет уходить в прошлое, wait_until немедленно вернется.

Не уверен, как и почему я это не заметил. Огромное спасибо!

It'sPete 10.09.2018 02:08

См. Мой отредактированный вопрос. Я не уверен, что wait_until () у меня сработает.

It'sPete 10.09.2018 20:05
Ответ принят как подходящий

Огромное спасибо Ховарду за то, что направил меня на правильный путь. В моем тестировании wait_for() действительно возвращается сразу после прохождения отрицательной продолжительности.

Вот код, который у меня получился:

void myFunctionWithTimeout(/*some other inputs*/ const long timeout_us) {
  const auto start_time = std::chrono::steady_clock::now();
  const auto end_time =
    start_time + std::chrono::duration<double, std::micro>(timeout_us);

  // Launch threads here using std::future and std::async...

  for(auto& worker : workers) {
    const auto current_timeout_us =
      std::chrono::duration<double, std::micro>(end_time - std::chrono::steady_clock::now());
    const auto status = worker.wait_for(current_timeout_us);
    // Check status and do the appropriate actions...
  }
}

Обратите внимание, что wait_until(), безусловно, является жизнеспособной альтернативой, но я слишком параноик в отношении изменений system_clock и поэтому использую монотонные часы.

Другие вопросы по теме