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;
}