colorful

zc qq:1337220912

 

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<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。

posted on 2012-03-30 16:39 多彩人生 阅读(2238) 评论(1)  编辑 收藏 引用

评论

# re: boost::asio::deadline_timer[未登录] 2012-11-27 17:22 lin

请教下
有没有支持多个定时器的timer呢?  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


导航

统计

常用链接

留言簿(3)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜