boost asio stable_timer 上的多个递归 async_wait

Joh*_*och 1 c++ boost boost-asio

这个问题的灵感来自于 boost asio 文档(链接)中的异步计时器教程。代码稍作修改,使效果更加明显。

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

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

  1. A具有通话steady_timer时长。1sasync_wait

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

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


#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;
}
Run Code Online (Sandbox Code Playgroud)

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

getting 20
getting 19
getting 18
...lines...
...omitted...
getting 3
getting 2
getting 1
21 seconds passed
Run Code Online (Sandbox Code Playgroud)

但是,如果上面代码中的两行未注释,则程序的行为会非常不同。输出粘贴在下面。该程序在一秒内打印从getting 20到 的所有行,40 秒内不显示任何内容,然后打印最后一行。getting 1

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
Run Code Online (Sandbox Code Playgroud)

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

Mat*_*247 5

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

该函数设置到期时间。任何挂起的异步等待操作都将被取消。每个取消操作的处理程序将使用 boost::asio::error::operation_aborted 错误代码来调用。

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

这种情况一直持续下去,直到处理程序经常取消自己count==0,并且只有一个计时器在运行。由于到期日期每次都向前推进 1 秒,因此代码仍会等待完整的 40 秒过去。