1:steady_timer的expires_from_now函数参数必须使用std::chrono
2:deadline_timer的expires_from_now函数参数必须使用boost::posix_time
声明以下处之别人的整理中
3:boost::asio::deadline_timer使用的计量时间是系统时间,因此修改系统时间会影响deadline_timer的行为。例如,调用了expires_from_now设置1分钟超时后,立刻把系统时间改成一天前,那么要过一天时间才会超时。这个特性可能会影响程序功能的正常使用,因此我们通常想要的是一个不会受系统时间影响的定时器。
事实上,boost::asio::steady_timer就是一个这样的定时器,它基于std::chrono::steady_clock实现。std::chrono::steady_clock是一个稳定的时钟,不随系统时间变化而变化。既然如此,直接用steady_timer代替deadline_timer不就可以了吗?理论上来说是可以的,但实际上,在Visual C++ 2013环境下,这是行不通的,因为Visual C++ 2013标准库中的std::chronno::steady_clock并不符合标准,它仍然会受系统时间影响!
有三种方法可以解决这个问题。第一是升级到Visual C++ 2015,这个版本的std::chronno::steady_clock总算符合标准了;第二是修改boost的编译选项,定义BOOST_ASIO_DISABLE_STD_CHRONO宏,这样可以禁止boost使用std::chrono,转而使用boost::chrono;第三是本文要介绍的方法,即定制deadline_timer,让它变成稳定的定时器。
deadline_timer实际上是basic_deadline_timer的特化版本,它的定义如下:
typedef basic_deadline_timer
basic_deadline_timer是一个模板类,它的定义如下:
template<
typename Time,
typename TimeTraits = boost::asio::time_traits
/// Time traits specialised for posix_time.
template <>
struct time_traits
{
/// The time type.
typedef boost::posix_time::ptime time_type;
/// The duration type.
typedef boost::posix_time::time_duration duration_type;
/// Get the current time.
static time_type now()
{
#if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
return boost::posix_time::microsec_clock::universal_time();
#else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
return boost::posix_time::second_clock::universal_time();
#endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
}
/// Add a duration to a time.
static time_type add(const time_type& t, const duration_type& d)
{
return t + d;
}
/// Subtract one time from another.
static duration_type subtract(const time_type& t1, const time_type& t2)
{
return t1 - t2;
}
/// Test whether one time is less than another.
static bool less_than(const time_type& t1, const time_type& t2)
{
return t1 < t2;
}
/// Convert to POSIX duration type.
static boost::posix_time::time_duration to_posix_duration(
const duration_type& d)
{
return d;
}
};
可以看到,TimeTraits需要提供time_type和duration_type两种类型来分别表示一个时间点和一段时间;需要提供now方法来获取当前时间;需要提供add、subtract和less_than方法来计算和比较时间;最后还需要to_posix_duration方法把duration_type类型转换成boost::posix_time::time_duration类型。
显然,对于定制的basic_deadline_timer,时间值类型Time可以是任意类型,并且它的含义并没有硬性规定,例如,它可以是以毫秒或纳秒为单位的时间,也可以是CPU时钟的周期数,只要提供了正确的TimeTraits特性类把这个定制的时间值转换成boost认识的时间值即可。
接下来要选择一种与系统时间无关的时间值类型来定制basic_deadline_timer。在Windows平台下,很容易想到可以使用QueryPerformanceCounter和QueryPerformanceFrequency,这两个API提供了高精度的时间度量,与系统时间无关。QueryPerformanceCounter用来查询当前CPU时钟的周期数,是64位整数,这个是理想的时间值类型。要把CPU时钟周期数转换成具体的时间还需要调用QueryPerformanceFrequency查询CPU时钟的频率,即1秒内的CPU时钟周期数,然后通过简单的计算即可得到。
下面的是使用QueryPerformanceCounter和QueryPerformanceFrequency定制的TimeTraits:
class TimeTraits {
public:
typedef std::int64_t time_type;
class duration_type {
public:
duration_type() : value(0) { }
duration_type(std::int64_t value) : value(value) { }
std::int64_t value;
};
public:
static void Initialize() {
LARGE_INTEGER frequence_large_integer = { 0 };
QueryPerformanceFrequency(&frequence_large_integer);
frequence = frequence_large_integer.QuadPart;
}
static duration_type GetMinutes(std::int64_t minutes) {
return duration_type(minutes * 60 * frequence);
}
static duration_type GetSeconds(std::int64_t seconds) {
return duration_type(seconds * frequence);
}
static duration_type GetMilliseconds(std::int64_t milliseconds) {
return duration_type(milliseconds * (frequence / 1000));
}
static time_type now() {
LARGE_INTEGER counter = { 0 };
QueryPerformanceCounter(&counter);
return counter.QuadPart;
}
static time_type add(time_type time, duration_type duration) {
return time + duration.value;
}
static duration_type subtract(time_type time1, time_type time2) {
return duration_type(time1 - time2);
}
static bool less_than(time_type time1, time_type time2) {
return time1 < time2;
}
static boost::posix_time::time_duration to_posix_duration(duration_type duration) {
std::int64_t microseconds = (duration.value * 1000 * 1000) / frequence;
return boost::posix_time::microseconds(microseconds);
}
private:
TimeTraits();
private:
static std::int64_t frequence;
};
CPU时钟的频率是固定的,只需要调用QueryPerformanceFrequency查询一次即可,因此这里增加了Initialize方法来初始化CPU时钟频率,要在程序启动后的某个时机来调用该方法进行初始化。要注意的是duration_type不能跟time_type相同,这是TimeTraits的硬性规定,boost内部的代码依赖了这个规定,违反它会导致编译失败。所以要针对duration_type额外定义一个看似冗余的类型。
另一个值得注意的地方是,上面的定义增加了GetMinutes、GetSeconds和GetMilliseconds方法,这几个方法用来将具体的时间转换成我们定制的时间值,即CPU时钟周期数。这是因为在调用定时器的expires_from_now等方法设置超时值的时候,必须使用TimeTraits的duration_type,提供这几个方法可以方便使用。
最后,将这个定制的TimeTraits作为basic_deadline_timer的模板参数即可:
typedef boost::asio::basic_deadline_timer
使用方法基本上与dealine_timer一样,如下所示:
//在某个时机初始化TimeTraits
TimeTraits::Initialize();
//在某个地方定义io_service
boost::asio::io_service io_service;
//使用定制的Timer
Timer timer(io_service);
timer.expires_from_now(TimeTraits::GetSecnods(10));
timer.wait();
|
|