У меня есть функция, которая принимает количество микросекунд до истечения времени ожидания, как long
. Этот тайм-аут - это тайм-аут для функции, чтобы завершить свою работу, даже если функция может занять больше времени, чем тайм-аут, из-за таких вещей, как планирование и другие накладные расходы.
Функция выполняет следующее:
std::future
и std::async
.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();
}
}
Мои вопросы:
double
, чтобы в различных вычислениях я мог учитывать доли микросекунды. Обратите внимание, что я знаю, что wait_for()
точно не будет ждать указанной мной продолжительности из-за планирования и прочего, но, по крайней мере, я не хочу добавлять ошибку округления в свои вычисления.std::chrono::duration
? Я хочу сохранить оставшееся время как продолжительность, а затем вычесть из него время настройки или время ожидания.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()
, дайте мне знать.
Функции ожидания, оканчивающиеся на _for
, должны немедленно возвращаться при отрицательной продолжительности.
Просто используйте wait_until
вместо wait_for
. Вычислите time_point
, который вы хотите подождать хотя бы один раз, и продолжайте его использовать. Если этот time_point
начнет уходить в прошлое, wait_until
немедленно вернется.
Не уверен, как и почему я это не заметил. Огромное спасибо!
См. Мой отредактированный вопрос. Я не уверен, что wait_until () у меня сработает.
Огромное спасибо Ховарду за то, что направил меня на правильный путь. В моем тестировании 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
и поэтому использую монотонные часы.
Да, возможно,
system_clock
вашего компьютера откатывается на час назад. Но это не должно происходить регулярно. Вашsystem_clock
отслеживает UTC, а не местное время.system_clock
обычно настраивается несколько раз в день с небольшими долями секунды, поскольку ваш компьютер, вероятно, устанавливает свойsystem_clock
с использованием протокола NTP. Даже дополнительные секунды обычно «размазываются» в течение нескольких часов, регулируяsystem_clock
на малые доли секунды за раз.