【问题标题】:Keeping Track of Timeout Using std::chrono::duration使用 std::chrono::duration 跟踪超时
【发布时间】:2018-09-09 22:20:25
【问题描述】:

我有一个函数,它以long 的形式接收超时发生前的微秒数。此超时是函数完成其工作的超时,即使由于调度和其他开销等原因,该函数可能需要比超时更长的时间。

该函数执行以下操作:

  1. 执行一些设置并使用std::futurestd::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),我认为它对我不起作用:

使用绑定到 timeout_time 的时钟,它不需要是单调时钟。如果时钟不连续调整,则无法保证此函数的行为,但现有实现将 timeout_time 从 Clock 转换为 std:: chrono::system_clock 并委托给 POSIX pthread_cond_timedwait 以便等待尊重对系统时钟的调整,而不是对用户提供的时钟的调整。在任何情况下,由于调度或资源争用延迟,该函数也可能等待比达到 timeout_time 更长的时间。

我读到的方式是,即使我使用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 通常每天会在几分之一秒内调整几次,因为您的计算机可能会使用 NTP 协议设置其 system_clock。通过一次将system_clock 调整几分之一秒,即使闰秒通常也会在几个小时内“拖尾”。
  • _for 结尾的等待函数应该立即返回负持续时间。

标签: c++ c++11


【解决方案1】:

只需使用wait_until 而不是wait_for。计算你想等到一次的time_point,然后继续使用它。如果 time_point 开始落入过去,wait_until 将立即返回。

【讨论】:

  • 不确定我如何/为什么忽略了这一点。非常感谢!
  • 查看我编辑的问题。我不确定 wait_until() 是否适合我。
【解决方案2】:

非常感谢霍华德让我走上正轨。在我的测试中,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 的变化有点过于偏执,因此我使用的是单调时钟。

【讨论】:

    猜你喜欢
    • 2018-02-03
    • 2016-07-20
    • 2023-01-11
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多