muduo的EventLoop.runAfter()的一次错误使用记录
问题出现
项目中使用loop.runAfter(60, …)设置一个定时任务,但是发现预期的效果没有出现,总是感觉定时任务调用时已经过了60秒,很奇怪,加了打印,发现:
loop.runAfter(60, ...), 实际调用时已经过了107~108秒
loop.runAfter(90, ...), 实际调用时已经过了154~158秒
EventLoop一直在使用,之前没有遇到过这种问题啊。反复使用后就是那样。
寻找其他做法
因为项目第二天就要提测,比较急,我就想能否有其他的代替方案,首先想到了boost.asio,这个东西之前用过,在有了muduo之后就没怎么用了。
利用boost::asio::io_service和boost::asio::deadline_timer实现runAfter应该没什么问题(自己还是太年轻,呵呵~),写了几个简单的例子,好像还真的可以。
试试加入多个任务,t_.expires_from_now(boost::posix_time::seconds(10));t_.async_wait(boost::bind(&Test::fun, this));
,问题出现了,boost::asio::deadline_timer中async_wait的任务总是立刻返回之前的任务,什么原因?
网上查了才发现自己对boost::asio::deadline_timer理解错了。boost::asio::deadline_timer
- cancel的使用async_wait设定的handler会被调用, 超时处理函数应该区分 boost::asio::error::operation_aborted
- 多次调用async_wait设定多个handler,则超时的时候它们都会被调用,如果将同一个handler设定多次,它也会被调用多次
- 在timer未超时前又调用expires_from_now相当于调用cancel
- 超时时间重新设定后,必须async_wait重新设定handler
总的来说一句话,boost::asio::deadline_timer只能维护一个定时任务,如果再加入定时任务,之前的就立刻返回了。
看来这条路走不通。
回到原点,解决问题
人在走投无路的时候才会孤注一掷,没有了其他的花花肠子,只能重新来看EventLoop.runAfter()出错的原因。
自己写了一个小例子:
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>
void fun() {
std::cout << "fun-" << time(NULL) << std::endl;
}
int main(int argc, char ** argv){
muduo::net::EventLoop loop;
std::cout << "main1-" << time(NULL) << std::endl;
loop.runAfter(20, fun);
loop.loop();
return 0;
}
20秒之后fun()被调用,打印日志没有问题啊,问题出在哪里呢?
既然这么调用没有问题,说明muduo没毛病啊,只能是我自己使用错了。
继续看看项目,我的项目中,loop干4件事,3个runAfter和1个runEvery,3个runAfter都有问题,runEvery却没有问题(我看了日志)。想想这次使用和上次使用的区别,没什么啊,不过就是runEvery有几个sleep(),不会是这个引起的吧?(曾经多少次想到这,都没有注意),现在也没有其他的办法,就写个例子测试一下吧:
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>
void fun() {
std::cout << "fun-" << time(NULL) << std::endl;
}
void fun2() {
std::cout << "fun2-1" << time(NULL) << std::endl;
sleep(30);
std::cout << "fun2-2" << time(NULL) << std::endl;
}
int main(int argc, char ** argv){
muduo::net::EventLoop loop;
std::cout << "main1-" << time(NULL) << std::endl;
loop.runAfter(20, fun);
std::cout << "main2-" << time(NULL) << std::endl;
loop.runAfter(10, fun2);
loop.loop();
return 0;
}
10秒以后没有输出,30秒以后全部输出。我懂了,原来是sleep搞的鬼。对啊,loop本质上就是一个线程,定时任务到期了,线程却在sleep,当然不能回调(脑子一下通了,之前肯定塞满了浆糊)。
那就是3个runAfter和另1个runEvery不能用同一个loop,runEvery中的sleep会影响runAfter的调用,使用两个loop。
项目中修改,编译,测试,没毛病。。。
教训和反思
- 不能放过一点蛛丝马迹
- 多想多测试