何时适合使用 C++11 *_until 超时函数而不是相应的 *_for 函数



在C++11中,只有当使用稳定的时钟(即仅以不变的速率向前移动的时钟)时,*_until超时函数的行为才会"按预期"。 因为system_clock不是一个稳定的时钟,这意味着像这样的代码可能会表现得非常令人惊讶:

using namespace std::chrono;
std::this_thread::sleep_until(system_clock::now() + seconds(10));

这将导致当前线程休眠 10 秒,除非在睡眠期间调整系统时钟,例如,夏令时。 如果在睡眠期间将时钟调回一小时,则当前线程将休眠一小时 10 秒。

据我所知,C++11 中的每个 *_until 超时函数都有一个相应的 *_for 函数,该函数需要持续时间而不是时间点。 例如,上面的代码可以重写如下:

using namespace std::chrono;
std::this_thread::sleep_for(seconds(10));

*_for函数不必担心在函数执行时调整的时钟,因为它们只是说等待多长时间,而不是等待结束时应该是什么时间。

此问题影响的不仅仅是睡眠函数,因为基于超时的 Future 和 try_lock 函数等待也是如此。

我能想象使用具有不稳定时钟的*_until函数的唯一情况是当您考虑时钟调整时,例如,您想睡到下周三凌晨 3:30,即使从现在到那时的夏令时有变化。 是否有其他情况*_until函数比*_for函数更有意义? 如果不是,是否可以肯定地说,一般来说,*_for超时函数应该优先于*_until函数?

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)一起使用的唯一情况是超时是用户可见的,并且取决于所选时钟的值。闹钟或提醒程序是一个例子,"午夜"运行的备份程序是另一个例子。

> sleep_until函数的一个用例是,如果你想睡到特定时间,而不是特定的持续时间。

例如,如果您有一个线程应该只在每天下午 3 点激活,则必须计算用于sleep_for的持续时间(包括处理夏令时和闰年),或者您可以使用 sleep_until

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,你将休眠你的周期加上运行循环代码所需的时间。

最新更新