Странное поведение таймера из boost::asio
В документации по бусту есть такой пример кода:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using namespace boost;
void print(const system::error_code& e, asio::steady_timer* timer, int count)
{
if (count < 5)
{
std::cout << count << std::endl;
++count;
timer->expires_at(timer->expiry() + asio::chrono::seconds(1));
timer->async_wait(boost::bind(print, asio::placeholders::error, timer, count));
}
}
int main()
{
asio::io_context io;
int count = 0;
asio::steady_timer t(io, asio::chrono::seconds(1));
t.async_wait(bind(print, asio::placeholders::error, &t, count));
io.run();
return 0;
}
Здесь, в функции main, таймеру задается начальное значение 5 секунд:
asio::steady_timer t(io, asio::chrono::seconds(5));
ДЛя чего нужно это начальное значение? Я заметил, что если этот параметр убрать, то есть сделать так:
asio::steady_timer t(io);
то по таймеру мгновенно выводятся все значения от 0 до 5, а не раз в секунду. Почему такое поведение? Почему для вывода значений раз в секунду должно быть начально значение таймера?
Ответы (1 шт):
У таймеров есть три вида конструкторов:
- Принимающие абсолютное время срабатывания.
- Принимающие относительное время срабатывания (относительно текущего).
- Не задающие время. Подразумевается, что время срабатывания будет задано потом.
asio::steady_timer t(io, asio::chrono::seconds(5)); относится ко второй категории, а asio::steady_timer t(io); - к третьей, однако время срабатывания вы не устанавливаете, оно остается равно началу отсчета часов, поэтому получается, что он истек уже в момент создания. Прибавляя к началу отсчета часов (вместо текущего времени) по секунде при вызове обработчика тоже получаются уже истекшие точки времени. Более наглядно происходящее можно понять, если написать обработчик нормально:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <chrono>
#include <iostream>
void print(::boost::system::error_code const & ec, ::boost::asio::steady_timer* timer, int count)
{
auto const when{::std::chrono::time_point_cast<::std::chrono::seconds>(timer->expires_at()).time_since_epoch()};
auto const now{::std::chrono::time_point_cast<::std::chrono::seconds>(::std::chrono::steady_clock::now()).time_since_epoch()};
::std::cout << "invoked at " << now << ", target " << when << "\n";
if (ec)
{
if (::boost::asio::error::operation_aborted == ec)
{
::std::cout << "timer cancelled\n";
}
else
{
::std::cout << "timer error " << ec << "\n";
}
}
else
{
if (when <= now)
{
::std::cout << "timeout\n";
std::cout << count << "\n";
++count;
if (count < 5)
{
timer->expires_at(timer->expiry() + ::boost::asio::chrono::seconds{1});
timer->async_wait(::boost::bind(&::print, ::boost::asio::placeholders::error, timer, count));
}
}
else
{
::std::cout << "misfire\n";
}
}
}
int main()
{
::boost::asio::io_context io{};
int count{};
::boost::asio::steady_timer t{io/*, ::boost::asio::chrono::seconds{1}*/};
t.async_wait(::boost::bind(&::print, ::boost::asio::placeholders::error, &t, count));
io.run();
return 0;
}
invoked at 8969s, target 0s
timeout
0
invoked at 8969s, target 1s
timeout
1
invoked at 8969s, target 2s
timeout
2
invoked at 8969s, target 3s
timeout
3
invoked at 8969s, target 4s
timeout
4