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

Posted

技术标签:

【中文标题】什么时候适合使用 C++11 *_until 超时函数而不是相应的 *_for 函数?【英文标题】:When is it appropriate to use a C++11 *_until timeout function instead of the corresponding *_for function? 【发布时间】:2012-06-27 09:11:39 【问题描述】:

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

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

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

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

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

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

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

我能想到的唯一一种情况是,当你想要考虑时钟调整时,例如,你想睡觉直到下周三凌晨 3 点 30 分,即使从现在到那个时候夏令时之间有变化。在其他情况下*_until 函数比*_for 函数更有意义吗?如果不是,是否可以肯定地说,一般来说,*_for 超时函数应该优于 *_until 函数?

【问题讨论】:

那不取决于时钟稳定性吗? wait_forwait_until 是否真的打算在处理“时钟跳跃”时有所不同? @K-ballo:对,这就是为什么我特别提到时钟稳定性和 system_clock 不稳定的原因。 _for_until 函数在时钟调整方面表现不同,但我不清楚意图是什么。 system_clock稳定性实际上是依赖于实现的。 @K-ballo:是的,你是对的。我应该说 system_clock 不能保证是稳定的。然而,我的理解是 system_clock 应该对应于挂钟时间,这意味着在观察夏令时的语言环境中,它将向前和向后调整,并且有时人们会期望它被调整为补偿本地机器上的时间漂移​​。在实践中,我认为 system_clock 保持稳定是不常见的。 std::this_thread::sleep_for(seconds(10));使用稳定的时钟而不是 system_clock,因此您在原始问题中声称的实际上是相等的 - 取决于实现。 【参考方案1】:

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

【讨论】:

【参考方案2】:

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 函数的大多数用例都将使用稳定的时钟,以实现可预测的超时。

我可以想象使用具有非稳定时钟(例如 std::chrono::system_clock)的 xxx_until 函数的唯一情况是超时是用户可见的,并且取决于所选时钟的值。闹钟或提醒程序是一个例子,“午夜”运行的备份程序是另一个例子。

【讨论】:

【参考方案3】:

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

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

【讨论】:

闹钟是一个很好的例子,说明应该考虑夏令时。您的工作时间已根据 DST 进行了调整,因此您的闹钟也应如此。

以上是关于什么时候适合使用 C++11 *_until 超时函数而不是相应的 *_for 函数?的主要内容,如果未能解决你的问题,请参考以下文章

什么时候适合使用 NOLOCK?

什么时候适合使用 std::optional

Bash 中“while”和“until”的区别

C# Selenium:使用 wait.Until 时无法捕获异常

[Java]_[初级]_[如何调用外部命令获取输出并设置它的超时退出]

[Java]_[初级]_[如何调用外部命令获取输出并设置它的超时退出]