Когда целесообразно использовать функцию тайм-аута С++ 11 * _until вместо соответствующей функции * _for?
В С++ 11 функции тайм-аута *_until
ведут себя "как ожидалось", только если используются устойчивые часы (т.е. тот, который перемещается только вперед с неизменной скоростью). Поскольку system_clock
не является постоянным, это означает, что такой код может вести себя довольно удивительно:
using namespace std::chrono;
std::this_thread::sleep_until(system_clock::now() + seconds(10));
Это заставит текущий поток спать в течение 10 секунд, если системные часы не будут отрегулированы во время периода ожидания, например, для летнего времени. Если часы установлены в течение часа во время сна, текущая нить будет спать в течение часа и 10 секунд.
Из того, что я могу сказать, каждая функция тайм-аута *_until
в С++ 11 имеет соответствующую функцию *_for
, которая занимает длительность вместо временной точки. Например, приведенный выше код можно переписать следующим образом:
using namespace std::chrono;
std::this_thread::sleep_for(seconds(10));
Функции *_for
не должны беспокоиться о часах, которые настраиваются во время выполнения функции, потому что они просто говорят, как долго ждать, а не время, которое должно было быть, когда ожидание закончилось.
Эта проблема затрагивает больше, чем функции сна, поскольку то же самое верно для ожидающих ожидания на функциях фьючерсов и try_lock.
Единственная ситуация, в которой я могу представить себе смысл использовать функцию *_until
с нестационарными часами, - это когда вы хотите принять во внимание синхронизацию часов, например, вы хотите спать до следующей среды в 3:30 утра, даже если время от времени происходит переход на летнее время или с летнего времени. Существуют ли другие ситуации, когда функции *_until
имеют больше смысла, чем функции *_for
? Если нет, можно ли с уверенностью сказать, что в функции *_until
должны быть предпочтительнее функции тайм-аута *_for
?
Ответы
Ответ 1
Звонки xxx _until
предназначены для тех случаев, когда у вас установлен крайний срок. Типичный пример использования - это то, где у вас есть жесткий временной интервал для раздела кода, который либо содержит несколько ожиданий, либо время, затрачиваемое каждым шагом до ожидания, непредсказуемо.
например.
void foo() {
std::chrono::steady_clock::time_point const timeout=
std::chrono::steady_clock::now()+std::chrono::milliseconds(30);
do_something_which_takes_some_time();
if(some_future.wait_until(timeout)==std::future_status::ready)
do_something_with(some_future.get());
}
Это будет обрабатывать только значение из some_future
, если оно готово в течение 30 мс с самого начала, включая время, затраченное на do_something_which_takes_some_time()
.
Как и в этом примере, большинство случаев использования функций xxx _until
будут использовать устойчивые часы, чтобы иметь предсказуемый таймаут.
Единственный случай, когда я могу представить, используя функции xxx _until
с нестационарными часами (например, std::chrono::system_clock
), - это время, когда тайм-аут является видимым пользователем и зависит от значения выбранных часов. Одним из примеров является программа будильника или напоминания, другая программа резервного копирования, которая запускается "в полночь".
Ответ 2
Один прецедент для функции sleep_until
- это если вы хотите спать до определенного времени, а не определенной продолжительности.
Например, если у вас есть поток, который должен активироваться только в 3 часа дня каждый день, вам нужно либо рассчитать продолжительность (включая обработку летнего и летнего времени) для использования с sleep_for
, либо вы можете использовать sleep_until
.
Ответ 3
Одно хорошее применение для sleep_until
- в циклах с постоянным временем (например, в игровых циклах). Если вы не уверены, сколько времени займет цикл для обработки, но он, как правило, должен иметь определенную минимальную длину, вы можете увеличить значение time_point, которое должно спать, до цикла. Например:
// requires C++14
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;
using namespace std::chrono;
using namespace std::literals;
int main()
{
auto start_time = steady_clock::now();
for (auto i = start_time; i <= start_time + 1s; i += 50ms) {
this_thread::sleep_until(i);
cout << "processing cycle..." << endl;
}
return 0;
}
Но тогда вам, вероятно, придется следить за задержкой, когда цикл занимает больше времени, чем приращение.
Идея состоит в том, что если вы наивно sleep_for
, вы будете спать с периодом цикла плюс время, затрачиваемое на выполнение кода внутри цикла.