boost::asio::deadline_timer

  • Post author:
  • Post category:其他


注意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<boost::asio::deadline_timer> t,

boost::shared_ptr<boost::asio::io_service> 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<boost::asio::io_service> io(new boost::asio::io_service);

boost::shared_ptr<boost::asio::deadline_timer> 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<boost::asio::io_service::work> 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:


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