boost::asio::deadline_timer

注意deadline_timer和socket一样,都用 io_service作为构造函数的参数。也即,在其上进行异步操作,都将导致和io_service所包含的iocp相关联。这同样意味着在析构 io_service之前,必须析构关联在这个io_service上的deadline_timer。
一个deadline_timer只维护一个超时时间,一个deadline_timer不同时维持多个定时器。

void wait(); void wait(boost::system::error_code & ec);

这是个同步等待函数,例如:
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
t.wait();
由于不涉及到异步,该函数和io_service没什么关系。这个函数在windows下的实现就只是简单的Sleep。因此也就不存在cancel之说。
如果t的expire时间已过,那么t.wait会立刻返回。
例如如下代码:
boost::asio::io_service io; boost::asio::deadline_timer t(io, boost::posix_time::seconds(5)); t.wait(); t.wait();

第一个t.wait会等待5s才返回,第2个t.wait会立刻返回。
wait函数本身没有参数,不存在t.wait(seconds(5))的用法。
可以在构造deadline_timer时指定时间。
basic_deadline_timer( boost::asio::io_service & io_service); basic_deadline_timer( boost::asio::io_service & io_service, const time_type & expiry_time); basic_deadline_timer( boost::asio::io_service & io_service, const duration_type & expiry_time);

注意后两种的区别。以下2种用法是等价的:
boost::asio::deadline_timer t(io, boost::posix_time::microsec_clock::universal_time()+boost::posix_time::seconds(5));
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
前者是绝对时间,后者是相对时间。
除了在deadline_timer构造函数中指定时间,也可以使用如下2个函数指定时间:
expires_at,expires_from_now。这两个函数的区别是前者参数是绝对时间,后者是相对时间。例如:
boost::asio::io_service io;
boost::asio::deadline_timer t(io);
t.expires_from_now(boost::posix_time::seconds(5));
t.wait();
注意这两个函数除了设定下次超时时间之外,还有一个效果是取消前面所有的异步wait。详情参看关于这两个函数的详细解释。
template<
typename WaitHandler>
void async_wait(
WaitHandler handler);
其中void handler(
const boost::system::error_code& error // Result of operation.
);
注意这个error很重要,表明这个handler是因为超时被执行还是因为被cancel。

符合2种情况之一,handler被执行:超时或者被cancel。

这同时隐含的说明了除非io.stop被调用,否则handler一定会被执行。即便是被cancel。

被cancel有多种方法,直接调用cancel或者调用expires_at,expires_from_now重新设置超时时间。

void handle_wait(const boost::system::error_code& error, boost::asio::deadline_timer& t,int& count) { if(!error) { std::cout<< count<<"\n"; if(count++<5) { t.expires_from_now(boost::posix_time::seconds(1)); t.async_wait(boost::bind(handle_wait,boost::asio::placeholders::error, boost::ref(t),boost::ref(count))); } } } int main() { boost::asio::io_service io; boost::asio::deadline_timer t(io); size_t a = t.expires_from_now(boost::posix_time::seconds(1)); int count = 0; t.async_wait(boost::bind(handle_wait,boost::asio::placeholders::error, boost::ref(t),boost::ref(count))); io.run(); return 0; }

deadline_timer的析构函数什么也不做,因此不会导致发出的async_wait被cancel。
std::size_t cancel();

std::size_t cancel(
boost::system::error_code & ec);
此函数调用会导致所有尚未返回的async_wait(handler)的handler被调用,同时error_code为boost::asio::error::operation_aborted。返回值是被cancel的timer数量。
time_type expires_at() const; std::size_t expires_at( const time_type & expiry_time); std::size_t expires_at( const time_type & expiry_time, boost::system::error_code & ec);

duration_type expires_from_now() const; std::size_t expires_from_now( const duration_type & expiry_time); std::size_t expires_from_now( const duration_type & expiry_time, boost::system::error_code & ec); 以上2组函数用来设置新的超时时间,同时cancel所有未完成的async_wait操作。注意这两个函数的返回值即为cancel的操作数量。

考虑如下场景,我们有一个workerthread正在调用io_work.run();

此时主线程向workerthread发出了一个异步调用,例如post(...),考虑到io_work.run很可能积压了很多handlers没有处理,或者某些handlers处理非常耗时,希望它在5s内必须返回。那么可以:
void handle_wait(const boost::system::error_code& error,bool& Ret)
{
if(!error) Ret = false;
}
void handle_func(
boost::shared_ptr t,
boost::shared_ptr io,
int* v)
{
boost::asio::io_service::work work(*io);
if(t->cancel()>0)
{
*v = 1;
}
}
void func_delay_1_second()
{
boost::asio::io_service io;
boost::asio::deadline_timer t(io,boost::posix_time::seconds(1));
t.wait();
}
bool sync_func(int& v,boost::asio::io_service& io_work)
{
boost::shared_ptr io(new boost::asio::io_service);
boost::shared_ptr t(new boost::asio::deadline_timer(*io));
t->expires_from_now(boost::posix_time::seconds(5));
bool ret = true;
t->async_wait(boost::bind(handle_wait,boost::asio::placeholders::error,boost::ref(ret)));
io_work.post(boost::bind(handle_func,t,io,&v));
io->run();
return ret;
}
int main()
{
boost::asio::io_service io_work;
auto_ptr work(new boost::asio::io_service::work(io_work));
boost::thread workthread(boost::bind(&boost::asio::io_service::run, &io_work));
for(int i=0; i<3; ++i) io_work.post(func_delay_1_second);
int v = 0;
bool ret = sync_func(v,io_work);
if(ret) printf("v %d\n",v);
【boost::asio::deadline_timer】work.reset();
workthread.join();
return 0;
}
上面代码中如果先进入handle_wait,则表明超时,此时设置ret = false,然后io.run会退出,表明调用失败,如果稍后进入handle_func,则t->cancel会返回0,也不做任何操作。虽然在 io.run退出时会释放v,但由于handle_func不做任何操作因此也不会引起任何安全问题。如果handle_func先进入,则首先使用 work让io.run不会退出,然后取消timer,并且设置,随后work析构,io.run会退出。注意这里面的同步问题:如果先进入 handle_wait,随后进入handle_func,那么handle_func中的t->cancel会返回0从而不做任何事。如果先进入 handle_func,随后进入handle_wait,那么t->cancel或者返回0或者返回1,由于使用了work,io.run也不会 退出。注意这里的t和io都是shared_ptr的,否则因为如果handle_wait先返回,则io.run会立刻退出并析 构,handle_func中将使用悬空的t和io,将导致非法操作。注意这里的io必须是shared_ptr的,如果 boost::asio::io_service::work work(*io); 改成work(t->get_ioservice());则t是有效的,而t所索引的io_service已经无效了,同样会导致非法操作。牢记 io_service的使用原则:必须首先析构所有索引的其他对象之后才能析构io_service。

取消deadline_timer方法:
  1. 通过cancel可以使waithandler立刻返回。所以必须在waithandler中检查errorcode,如果errorcode为0,那么执行需要的定时操作,如果errorcode不等于0,那么检查其是否等于boost::asio::error::operation_aborted,如果等于,那么表示timer是被cancel。
  2. deadline_timer析构的开销很小。
example:
waithandler:

[cpp] view plain copy print ?
  1. void checkZipVector( server_connection_type& conn, const boost::system::error_code& error )
  2. {
  3. if(!error)
  4. {
  5. LOG4CXX_INFO(m_logger, boost::format("[AsyncTimer] Begin zip transaction: %1%") % m_zipCurVecLen);
  6. asyncWriteZipData(conn);
  7. m_timer->expires_from_now(boost::posix_time::seconds(m_timeout));
  8. m_timer->async_wait(boost::bind(&server_work::checkZipVector, shared_from_this(), boost::ref(conn), boost::asio::placeholders::error));
  9. }
  10. else if(error == boost::asio::error::operation_aborted)
  11. {
  12. LOG4CXX_INFO(m_logger, "[AsyncTimer] Cancel");
  13. }
  14. }

    推荐阅读