c++ - boost asio steady_timer 上的多个递归 async_wait

标签 c++ boost boost-asio

这个问题的灵感来自 boost asio 文档 (link) 中关于异步定时器的教程。代码略作修改,效果更明显。

有一个相关的问题,Multiple async_wait from a boost Asio deadline_timer .但我不确定该问题的答案是否适用于我的情况。

如果重复的行被注释掉,代码非常简单并且可以按预期工作,如下所示。

  1. 持续时间为 1ssteady_timer 调用 async_wait

  2. 当它过期时,调用处理程序。在处理程序内部,定时器的生命周期再延长一秒,定时器再次调用 async_wait

  3. 变量 count 为 20 用于限制计时器可以触发的次数。


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

namespace asio = boost::asio;

void bind_handler(const boost::system::error_code& ec,
                  asio::steady_timer& t,
                  int count) {
  if (count > 0) {
    std::cout << "getting " << count << "\n";
    t.expires_at(t.expiry() + std::chrono::seconds(1));
    t.async_wait(boost::bind(bind_handler, asio::placeholders::error,
                             boost::ref(t), --count));
  }
}

int main() {
  asio::io_context io_context(1);
  asio::steady_timer t(io_context, std::chrono::seconds(1));

  int count = 20;

  t.async_wait(boost::bind(bind_handler, asio::placeholders::error,
                           boost::ref(t), count));
  //t.async_wait(boost::bind(bind_handler, asio::placeholders::error,
  //                         boost::ref(t), count));

  auto start = std::chrono::steady_clock::now();
  io_context.run();
  auto end = std::chrono::steady_clock::now();
  std::cout
      << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
      << " seconds passed\n";

  return 0;
}

此代码的输出如下所示。每过一秒就会打印一行。

getting 20
getting 19
getting 18
...lines...
...omitted...
getting 3
getting 2
getting 1
21 seconds passed

但是,如果上面代码中的两行没有注释,程序的行为就会大不相同。输出粘贴在下面。该程序在一秒内打印从 getting 20getting 1 的所有行,40 秒内什么都不显示,然后打印最后一行。

getting 20
getting 20
getting 19
getting 19
getting 18
getting 18
...lines...
...omitted...
getting 3
getting 3
getting 2
getting 2
getting 1
getting 1
41 seconds passed

我的问题是,async_wait 的多次递归调用如何影响程序的行为?我觉得正在进行某种数据竞赛,但数字仍然按顺序打印。此外,只涉及一个线程,正如我们在 io_context 构造函数中看到的那样。

最佳答案

似乎该行为的答案在于 basic_waitable_timer::expires_at(const time_point & expiry_time) 的文档中:

This function sets the expiry time. Any pending asynchronous wait operations will be cancelled. The handler for each cancelled operation will be invoked with the boost::asio::error::operation_aborted error code.

在您的示例中,当第一个计时器完成时,它会调用 expires_at 以便将计时器转发一秒钟。然而,这会取消第二次运行的 await 调用,现在将在下一次事件循环迭代中直接调用,并出现 operation_aborted 错误。但是,由于您不检查错误代码 ec,因此看不到它。现在这个handler会直接再次转发定时器,从而取消上一次启动的async_wait

这会继续下去,直到处理程序经常取消自己,以至于 count==0 并且只有一个计时器在运行。由于到期日期每次转发 1 秒,代码仍然等待整整 40 秒过去。

关于c++ - boost asio steady_timer 上的多个递归 async_wait,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54241257/

相关文章:

c++ - boost ASIO 和 co_await- 与任何第三方回调一起使用?

c++ - 在 C++ 中对字符 vector 进行排序并将大写和小写字母视为相等的最佳方法?

c++ - 静态断言类型 A 可以从类型 B 构造

c++ - btRigidBody 底部的子弹射线转换

c++ - Boost.Asio Win32 Windows 应用程序

c++ - 如何使用 boost::filesystem "normalize"路径名?

c++ - 使用 boost::asio 的 HTTP POST 请求

c++ - Boost.Asio 中的异常处理

java - Java和C++进程之间的通信

c++ - EqualityComparable 特质解释