Timer.3 绑定参数到 Completion Handler

Timer.3——将参数绑定到completion handler #


原文链接 | 源码链接

在本教程中,我们将修改教程 Timer.2 中的程序,以便定时器每秒触发一次。这里会展示如何将附加参数传递给 handler 函数。

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

要使用 asio 实现重复定时器,你需要在 completion handler 中更改定时器的到期时间,然后启动新的异步等待。显然,这意味着 completion handler 需要能够访问定时器对象。为此,我们向 print 函数添加两个新的参数:

  • 指向定时器对象的指针;
  • 一个计数器,以便我们可以在定时器第六次触发时停止程序。
void print(const boost::system::error_code& /*e*/,
    boost::asio::steady_timer* t, int* count)
{

如上所述,这个程序使用计数器在定时器第六次触发时停止运行。但是你会发现没有显式的函数调用让 io_context 停止。回顾教程 Timer.2 ,我们了解到当没有更多的“工作”需要处理时,boost::asio::io_context::run() 函数就会结束运行。如果不在定时器上启动新的异步等待,当计数达到 5 时,io_context 将会因为没有任务可做而停止运行。

  if (*count < 5)
  {
    std::cout << *count << std::endl;
    ++(*count);

接下来,我们将定时器的到期时间从之前的到期时间往后调整一秒钟。通过相对于旧的到期时间来计算新的到期时间,我们可以确保定时器不会因为 handler 处理过程中的任何延迟而偏离整秒的标记【注:即保持两次计时的间隔为准确的1秒】。

    t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));

随后,我们在定时器上启动一个新的异步等待。正如你看到的,std::bind 函数用于将额外的参数与 completion handler 绑定。steady_timer::async_wait() 函数期望 handler 函数(或函数对象)的签名为 void(const boost::system::error_code&)。绑定的额外参数可以将你的 print 函数转换为与签名相匹配的函数对象。

有关如何使用 boost::bind() 的更多信息,请参阅 Boost.Bind 文档

在这个例子中,boost::bind 中的 boost::asio::placeholders::error 参数是传递给 handler 的 error 对象的命名占位符。启动异步操作时,如果使用 boost::bind,则必须仅指定与 handler 参数列表匹配的参数【注:即与上一段提到的签名 void(const boost::system::error_code&) 匹配】。在教程 Timer.4 中,你会看到,如果 completion handler 不需要该参数,则这个占位符可能会被省略。

    t->async_wait(boost::bind(print,
          boost::asio::placeholders::error, t, count));
  }
}

int main()
{
  boost::asio::io_context io;

添加一个新的 count 变量,以便我们可以在定时器第六次触发时停止程序。

  int count = 0;
  boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1));

当在 main 函数中调用 steady_timer::async_wait() 时,我们绑定了 print 函数所需的附加参数。

  t.async_wait(boost::bind(print,
        boost::asio::placeholders::error, &t, &count));

  io.run();

最后,为了证明 count 变量正在被 print 的 handler 函数使用,我们打印出它最新的值。

  std::cout << "Final count is " << count << std::endl;

  return 0;
}