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

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

你可能感兴趣的:(boost::asio::deadline_timer)