【问题标题】:boost::asio::deadline_timer 1ms lags after some timeboost::asio::deadline_timer 1ms 滞后一段时间
【发布时间】:2018-04-11 07:47:06
【问题描述】:

我使用 boost::asio::deadline_timer 实现了一个计时器。 我使用 expires_from_now(boost::posix_time::milliseconds(1)) 运行计时器

我数了一下,它在 10 秒内触发的频率(在 Windows 下)。我预计会有 10 000 次。

结果如下: 在一台 PC 上的计数器非常准确 - 每 10 秒 10 000 次。 在其他 PC 计数器上随机变化在 7000 和 8500 之间。 问题:一段时间后,计数减少到每 10 秒 600-800 次。

当我仍然使用间隔为 1 毫秒的计时器时,我无法找出超时增加到 ~10-15 毫秒的原因。

我检查了电源管理中的处理器电源设置 - 最低性能为 100%。 Windows 中是否有任何其他设置可能会影响不同 PC 上的结果? 为什么在运行程序一段时间后会出现这种情况?

【问题讨论】:

  • deadline_timer 使用实时时钟,它可能没有您想要的精度。在多任务操作系统中,准确的定时并不容易实现。这里有进一步的目标吗?
  • 我不介意精度 +-1ms。最大的问题是为什么一段时间后这个精度从 +-1 毫秒跳到 10 毫秒。每 10 秒 8000 次和每 10 秒 800 次之间的差异不是准确性问题。操作系统中的一些设置会导致(处理器?)延迟......
  • 这完全取决于硬件和操作系统。
  • 这也取决于循环的编码方式。是什么让您期望您可以以零开销编写循环?

标签: c++ boost


【解决方案1】:

不要等待“x”毫秒,如果您依赖于以尽可能高的精度满足最后期限,只需绝对说明它们:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>

namespace ba = boost::asio;
using namespace std::chrono_literals;

int main() {
    ba::io_context io;

    using C = ba::high_resolution_timer::clock_type;
    ba::high_resolution_timer t(io);

    auto next_wakeup = [&t, interval = 10ms] {
        t.expires_at(C::now() + interval);
        t.wait();
    };

    auto until = C::now() + 5s;
    int count = 0;

    do count += 1;
    while (next_wakeup(), C::now() <= until);

    std::cout << "Timer triggered " << count << " times in 5s\n";
}

在我的系统上它报告 497,因此您可以看到循环开销足以错过几个截止日期。如果您降低频率,这将变得更加重要。

替代方法

您当然可以使事物成为多线程并跨线程分配计时器事件,这样错过的事件就会更少。或者你可以看看experimental scheduler in Boost Thread

如果您更改设计权衡以最大限度地减少错过的事件,代价是(可能)具有更嘈杂的频率/间隔:

请注意每次从起点计算下一个事件时如何小心,这样INTERVAL 的指定精度可能无法在时钟的time_point 中表示:

   auto constexpr INTERVAL = 1.0/3ms;

表示。否则可能会累积舍入误差。

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/thread.hpp>
using namespace std::chrono_literals;

namespace /*static*/ {
    auto constexpr INTERVAL = 1ms;
    auto constexpr DURATION = 5s;
    std::atomic_int count {0};

    void on_timer_event() { ++count; }
}

namespace ba = boost::asio;
using Timer = ba::high_resolution_timer;
using C = Timer::clock_type;

template <typename Interval>
static void timer_chain(Timer& t, C::time_point start_point, Interval ival, int n = 0) {
    t.expires_at(start_point + std::chrono::duration_cast<C::duration>(n * ival));

    t.async_wait([=,&t](auto ec) {
            if (!ec) {
                on_timer_event();
                timer_chain(t, start_point, ival, n+1);
            }
        });
}

#include <iostream>
int main() {
    ba::io_context io;
    boost::thread_group tg;

    std::list<Timer> timers;

    auto const slices = 10;
    auto const start_point = C::now();
    auto group_interval = INTERVAL * slices;

    for (auto slice = 0; slice<slices; ++slice)
        timer_chain(timers.emplace_back(io), start_point + slice*INTERVAL, group_interval);

    for (unsigned i = 0; i < std::thread::hardware_concurrency(); ++i)
        tg.create_thread([&io] { io.run_for(DURATION); });

    std::cout << "Running on " << tg.size() << " threads...\n";

    tg.join_all();

    std::cout << "Event triggered " << count << " times in " << (C::now() - start_point)/1ms << "ms\n";
}

打印出来

Running on 1 threads...
Event triggered 5002 times in 5001ms

或者,在我的系统上:

Running on 8 threads...
Event triggered 5002 times in 5001ms

【讨论】:

  • 添加了一个替代方案,显示不同的权衡(可能使用线程组)coliru.stacked-crooked.com/a/43d0e54bde5df9d3
  • 第二个变种是我需要的。具有绝对时间值而不是“等待”的高分辨率计时器,很好的解决方案!谢谢!
  • 注意警告:这是一种权衡。如果您绘制on_timer_event 运行的有效时间,您可能会发现更广泛的变化。由您决定什么更重要。
猜你喜欢
  • 1970-01-01
  • 2019-12-19
  • 1970-01-01
  • 2013-04-08
  • 1970-01-01
  • 2014-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多