You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改活跃Boost Asio定时器过期时间未达预期问题求助

重启Boost Asio定时器未达预期效果

问题描述

我基于Boost Asio的steady_timer相关逻辑编写代码,尝试重启定时器,但实际输出与预期不符。

当前输出

0 : Start at 
0 : ioContext.run()
1000 : on_timeout Timeout reached error=0
1100 : Too late, timer has already expired after 1100 ms
2200 : We managed to cancel the timer after 1100 ms
3300 : We managed to cancel the timer after 1100 ms
...

预期输出

0 : Start at 
0 : ioContext.run()
1000 : on_timeout Timeout reached error=0
1000 : Too late, timer has already expired after 1100 ms
2000 : on_timeout Timeout reached error=0
2000 : Too late, timer has already expired after 1100 ms
3000 : on_timeout Timeout reached error=0
3000 : Too late, timer has already expired after 1100 ms
...

问题代码

#include <chrono>
#include <functional>
#include <iostream>
#include <thread>

#include <boost/asio.hpp>

long int millis() {
  static long int offset = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
  return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() - offset;
}

class BoostAsioTimerEval {
 public:
  BoostAsioTimerEval(boost::asio::io_context& ioContext)
      : ioContext_(ioContext), timer_(ioContext_), interval_(std::chrono::milliseconds{1000}), lastMillis_(millis()) {
    timer_.expires_after(interval_);
    timer_.async_wait(std::bind(&BoostAsioTimerEval::on_timeout, this, std::placeholders::_1));
    std::cout << millis() << " : Start at " << std::endl;
  }

  void on_event() {
    if (timer_.expires_after(interval_) > 0) {
      std::cout << millis() << " : We managed to cancel the timer after " << (millis() - lastMillis_) << " ms" << std::endl;
      timer_.async_wait(std::bind(&BoostAsioTimerEval::on_timeout, this, std::placeholders::_1));
    } else {
      std::cout << millis() << " : Too late, timer has already expired after " << (millis() - lastMillis_) << " ms" << std::endl;
      // kick again
      timer_.expires_after(interval_);
      timer_.async_wait(std::bind(&BoostAsioTimerEval::on_timeout, this, std::placeholders::_1));
    }
    lastMillis_ = millis();
  }

  void on_timeout(const boost::system::error_code& error) {
    if (error != boost::asio::error::operation_aborted) {
      std::cout << millis() << " : on_timeout Timeout reached error=" << error.value() << std::endl;
    } else {
      std::cout << millis() << " : on_timeout Timeout aborted error=" << error.value() << std::endl;
    }
  }

 private:
  boost::asio::io_context& ioContext_;
  boost::asio::steady_timer timer_;
  std::chrono::milliseconds interval_;
  long int lastMillis_;
};

int main(int argc, char** argv) {
  boost::asio::io_context ioContext;
  BoostAsioTimerEval boostAsioTimerEval(ioContext);
  std::thread worker([&]() {
    for (int i = 0; i < 10; ++i) {
      const int sleep = 1100;
      std::cout << millis() << " : sleep for " << sleep << " ms" << std::endl;
      std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
      boostAsioTimerEval.on_event();
    }
    ioContext.stop();
  });
  std::cout << millis() << " : ioContext.run()" << std::endl;
  ioContext.run();
  worker.join();
  return 0;
}

问题分析与解决方案

核心问题是定时器到期后未自动重启:第一次定时器在1000ms到期后,on_timeout仅输出日志,没有重新绑定异步等待,导致后续需要等到1100ms时on_event触发才重新设置定时器,打乱了1000ms的间隔节奏。同时lastMillis_的更新时机也不符合预期逻辑。

修改后的代码

#include <chrono>
#include <functional>
#include <iostream>
#include <thread>

#include <boost/asio.hpp>

long int millis() {
  static long int offset = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
  return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() - offset;
}

class BoostAsioTimerEval {
 public:
  BoostAsioTimerEval(boost::asio::io_context& ioContext)
      : ioContext_(ioContext), timer_(ioContext_), interval_(std::chrono::milliseconds{1000}), lastMillis_(millis()) {
    restart_timer();
    std::cout << millis() << " : Start at " << std::endl;
  }

  void on_event() {
    if (timer_.expires_after(interval_) > 0) {
      std::cout << millis() << " : We managed to cancel the timer after " << (millis() - lastMillis_) << " ms" << std::endl;
      restart_timer();
    } else {
      std::cout << millis() << " : Too late, timer has already expired after " << (millis() - lastMillis_) << " ms" << std::endl;
      restart_timer();
    }
    lastMillis_ = millis();
  }

  void on_timeout(const boost::system::error_code& error) {
    if (error != boost::asio::error::operation_aborted) {
      std::cout << millis() << " : on_timeout Timeout reached error=" << error.value() << std::endl;
      lastMillis_ = millis();
      restart_timer();
    } else {
      std::cout << millis() << " : on_timeout Timeout aborted error=" << error.value() << std::endl;
    }
  }

 private:
  void restart_timer() {
    timer_.expires_after(interval_);
    timer_.async_wait(std::bind(&BoostAsioTimerEval::on_timeout, this, std::placeholders::_1));
  }

  boost::asio::io_context& ioContext_;
  boost::asio::steady_timer timer_;
  std::chrono::milliseconds interval_;
  long int lastMillis_;
};

int main(int argc, char** argv) {
  boost::asio::io_context ioContext;
  BoostAsioTimerEval boostAsioTimerEval(ioContext);
  std::thread worker([&]() {
    for (int i = 0; i < 10; ++i) {
      const int sleep = 1100;
      std::cout << millis() << " : sleep for " << sleep << " ms" << std::endl;
      std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
      boostAsioTimerEval.on_event();
    }
    ioContext.stop();
  });
  std::cout << millis() << " : ioContext.run()" << std::endl;
  ioContext.run();
  worker.join();
  return 0;
}

修改说明

  1. 封装restart_timer函数:把设置到期时间和绑定异步等待的逻辑抽成独立函数,避免重复代码。
  2. 到期后自动重启:在on_timeout的正常到期分支里,立即调用restart_timer,保证定时器持续按1000ms间隔触发。
  3. 修正lastMillis_更新时机:在定时器实际到期时更新该值,确保时间差计算准确。

修改后,定时器会在每次到期后自动重启,即使on_event延迟调用,输出也会符合预期的间隔节奏。

内容的提问来源于stack exchange,提问作者Th. Thielemann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:57:07