上一节已经完善了定时器容器类TimerQueue,该类内部有三个容器,一定要区分清楚这三个容器的作用。
这一节就来讲讲用户是如何去增添或删除定时器的。首先要明确TimerQueue是内部类,用户是看不见的,不能直接去调用的。而由之前写的main函数中可知外部可给用户使用的主要是EventLoop类,Server类。
int main()
{
//省略部分哈...........................
EventLoop loop;
Server server(servAddr, &loop);
server.start(2); //开启的io线程数量,主线程不包括在内的
loop.loop();
}
而我们的TimerQueue类中有个成员变量EventLoop类对象loop_,这个表明是在哪个loop线程去执行定时器。所以我们可以通过EventLoop类去调用TimerQueue类。
而TimerQueue类值提供了两个接口给外界使用:增加定时器的函数,删除定时器的函数。
那EventLoop类中也主要就关于这两个接口的调用。
1.通过EventLoop去增删定时器
EventLoop类中需要添加个成员变量std::unique_ptr<TimerQueue> timer_queue_,这样才能操作定时器队列呢。下面的就是需要对定时器队列操作的函数。
//在给定的时间执行定时器
int64_t EventLoop::runAt(Timestamp time, Timer::TimerCallback cb)
{
return timer_queue_->addTimer(std::move(cb), time, 0.0);
}
//在给定的时间间隔后执行定时器
int64_t EventLoop::runAfter(double delay_seconds, Timer::TimerCallback cb)
{
Timestamp time(addTime(Timestamp::now(), delay_seconds));
return runAt(time, std; :move(cb));
}
//每个一个时间间隔就执行一次定时器
int64_t EventLoop::runEvery(double interval_seconds, Timer::TimerCallback cb)
{
Timestamp time(addTime(Timestamp::now(), interval_seconds));
return timer_queue_->addTimer(std::move(cb), time, interval_seconds);
}
void EventLoop::cancel(int64_t timerId)
{
return timer_queue_->cancel(timerId);
}
这些在EventLoop类中新添加的函数也很好理解,从函数名字就可以知道该函数的作用,这些函数也是对TimerQueue::addTimer函数和TimerQueue::cancel函数进行简单的再次封装,方便用户的使用。
来看看使用的例子:
void myprint(const char* msg)
{
printf(" %s now:%s\n",msg, Timestamp::now().toFormattedString().c_str());
}
int main()
{
EventLoop loop;
loop.runAfter(1, []() {myprint("once1"); });
loop.runAfter(1.5, []() {myprint("once1.5"); });
loop.runAfter(2.5, []() {myprint("once2.5"); });
loop.runAfter(4.8, []() {myprint("once4.8"); });
loop.runEvery(2, []() {myprint("every2"); });
loop.loop();
}
结果:
2.TimerQueue类中的一些可能有的困惑
首先就是为什么该类需要有三个容器呢,感觉这样很麻烦。
确实是会有点麻烦,但这是为了能实现删除定时器,和支持定时器是可以重复的这两个要求。
要是定时器是不用删除的,那就直接使用一个优先队列就搞定的啦。
而我们选择了要删除的功能,就需要添加多个容器去解决如何快速地删除给定的定时器这个问题嘛。希望大家认真思考这其中的得与失。
接着还有一个,在TimerQueue::addTimer函数中,需要使用new来创建一个定时器,那我们可以选择使用智能指针吗?
是可以的。那我们先看看独享的智能指针std::unique_ptr。而我们有两个容器是存放同一个定时器的,那这个定时器就不能独享了,所以不能使用std::unique_ptr。
那共享的智能指针std::shared_ptr可以使用吗,那当然是可以使用的。
但是这使用可能会增加稍微的性能消耗吧,需要维护其引用计数。而Timer类是用户看不见的,用户不会去使用的。用户是通过传时间,是否重复,回调任务这三个参数给addTimer函数的,所以使用原始指针也不错的。
这一节增添的代码算是比较少的,主要就是在EventLoop类中增添几个有关TimerQueue类增删定时器的操作,方便用户的使用。那定时器功能到这里就可以结束了啦。
完整源代码:https://github.com/liwook/CPPServer/tree/main/code/server_v16