南昌易动力网站建设公司,开发app的软件,免费建站免费网站,wordpress 总访问量使用一个定时器(timer_fd)管理多个定时事件
使用 timerfd_xxx 系列函数可以很方便的与 select、poll、epoll 等IO复用函数相结合#xff0c;实现基于事件的定时器功能。大体上有两种实现思路#xff1a;
为每个定时事件创建一个 timer_fd#xff0c;绑定对应的定时回调函数…使用一个定时器(timer_fd)管理多个定时事件
使用 timerfd_xxx 系列函数可以很方便的与 select、poll、epoll 等IO复用函数相结合实现基于事件的定时器功能。大体上有两种实现思路
为每个定时事件创建一个 timer_fd绑定对应的定时回调函数然后将 timer_fd 注册到 epoll或其它IO复用函数中当 timer_fd 可读调用其回调函数然后关闭该文件描述符。只创建一个 timer_fd。管理所有定时事件timer_fd 每次只关注时间序列上下一个将要超时的时间当 timer_fd 变得可读从管理的所有定时事件中查找比 timer_fd 可读时刻小的定时事件然后执行对应的回调函数。
这两种方法中第一种实现起来相对简单但一个定时事件就对应一个文件描述符当定时事件较少且创建周期不频繁时该方法没啥问题但当定时事件较多且定时事件的创建和销毁频繁时会导致文件描述符的频繁创建和关闭影响服务器性能。第二种方法只使用一个 timer_fd 来管理所有定时事件能避免文件描述符频繁创建和关闭带来的系统影响但在实现上相对复杂关键在于如何高效地管理所有还未超时的定时事件。
下面将具体介绍第二种方法的实现思路和一些实现上的细节该思路主要来自 muduo 网络库的实现我尝试对其进行了一点点改进并将思考一并写在下文。
使用 timerfd_xxx 和 epoll 实现定时器的功能的主要逻辑如下面的流程图所示一图胜千言不再过多的文字解释。 下面介绍一些实现上的细节。
选用什么样的数据结构管理定时事件对于定时事件的添加、删除和查找要高效。因为只使用一个 timerfd 来管理多个定时事件而 timerfd 每次只能关注一个超时时间若每新添加一个定时事件就调用 timerfd_settime 设置超时事件会使前面的定时事件失效。因此一个自然的想法是根据定时事件的超时时间从小到大排序timerfd 每次只关注所有定时事件中超时时间最小的哪个时间。可以使用C标准库中的 set 或 map 来管理定时事件它们是有序集合底层的数据结构为红黑树插入、删除和查找的平均时间复杂度都为 O(log N)muduo 中就是使用 set 来管理定时事件的。
muduo 中的做法是使用 set 来管理定时事件set 中的元素类型为 pairTimestampTimer*。书中给出了采用这种做法的原因 “不能直接用 mapTimestampTimer因为这样无法处理两个Timer到期时间相同的情况。有两个解决方案一是用 multimap 或 multiset二是设法区分key。muduo现在采用的是第二种做法这样可以避免使用不常见的 multimap class。具体来说以 pairTimestampTimer 为key这样即便两个Timer的到期时间相同它们的地址也必定不同。”
我在写定时器这部分功能时采用的是 muduo 中提到的第二种方法使用 multimap 管理定时事件。muduo 中 Timestamp 的精度为微妙我的实现中 Timestamp 的精度为纳秒因此几乎不可能存在两个 Timer 到期事件相同的情况即便存在两个 Timer 到期的 Timestamp 相同也无妨紧要因为我在 Timer 类中添加了 TimerId 成员变量用来唯一标识 Timer可以通过成员函数来获取该标识。
如下TimerId、Timer和TimerQueue类的定义所示TimerQueue中的 activeTimers_ 成员变量为 set 类型其元素为 TimerId 类型而 Timer 中具有获取 TimerId 成员变量的成员函数因此在 TimerQueue 类中timers_、activeTimers_ 和 cancelingTimers_ 三个成员变量可以很方便地进行相互转换查找。
class TimerId
{
public:friend class TimerQueue;public:TimerId(): id_(0), timer_(nullptr) {}TimerId(int64_t id, Timer* timer): id_(id), timer_(timer) {}// ...private:int64_t id_;Timer* timer_;
};class Timer
{
public:Timer(TimerCallback cb, Timestamp when, Seconds interval): callback_(std::move(cb)), expiration_(when), interval_(interval), repeat_(interval_ 0.0),id_(timerCount_, this){}// ...private:TimerCallback callback_;Timestamp expiration_;Seconds interval_; bool repeat_;const TimerId id_; // 定时器唯一标识static std::atomic_int64_t timerCount_;
};class TimerQueue
{
public:using TimerMap std::multimapTimestamp, std::unique_ptrTimer;using TimerVector std::vectorstd::unique_ptrTimer;using ActiveTimer std::setTimerId;explicit TimerQueue(EventLoop *loop);// ...private:EventLoop *loop_;int timerfd_;Channel timerChannle_;TimerMap timers_;ActiveTimer activeTimers_;std::setTimer* cancelingTimers_;std::atomic_bool callingExpiredTimers_;
}; 在确定了管理定时事件的数据结构后按照上述所给的定时事件的处理流程来编写代码即可。在实现细节上muduo 源码中对于上层应用删除一个定时器的实现我觉得处理的很到位在这里单独拿出来描述。
定时器对上层提供的接口无非就两类一类是添加一个定时事件另一类则是删除一个定时事件。添加一个定时事件的处理相比于删除一个定时事件要简单些只需往 TimerMap 中插入一个 Timer 即可然后判断一下添加的 Timer 的超时时间是否小于当前设置的超时时间若小于则更新定时器的超时时间。这一步的实现很简单因为 TimerMap 为 map 类型只需 O(1) 的时间复杂度即可完成。
而对于删除一个定时事件要复杂一些。
首先对于定时事件超时在处理完定时事件上的回调函数后若超时的定时事件不是周期性的需要能够自动删除。我通过 unique_ptr 来实现定时事件的自动删除这也是 muduo 中提到的改进方法。当有定时事件发生将超时的事件从 TimerMap move 到一个 vector 中然后处理超时事件的回调函数处理完后若是周期性事件则将其再次 move 到 TimerMap 中若不是周期性事件则什么都不用做unique_ptr 管理的 Timer 会自动随着 vector 的析构而析构。
其次对于上层调用删除指定实时事件需要考虑这样一种场景当具有周期性的定时事件超时后且 正在处理定时事件的回调函数 时上层应用调用删除指定定时事件的函数且这个待删除的定时事件为正在超时处理的周期性定时事件。此时就不能简单的直接根据超时的定时事件为周期性事件就再次将其添加回 TimerMap 中要同时判断其是否正在被删除。这也是为什么在 TimerQueue 类中需要具有一个 cancelingTimers_ 和 callingExpiredTimers_ 成员变量的原因也是 muduo 中处理很巧妙的一部分。
具体的实现思路为
使用 callingExpiredTimers_ 成员变量标识是否正在处理超时事件的回调函数。 callingExpiredTimers_ true;cancelingTimers_.clear();// 处理超时事件的回调函数for (const auto iter : expirationTimers) {iter-run();}callingExpiredTimers_ false;在上层应用调用删除定时事件的函数时若删除的定时事件已超时且正在执行超时回调函数则将其添加到 cancelingTimers_ 中。 // 若定时事件未超时则可以直接从 TimerMap 中删除if (iter ! activeTimers_.end()){// ...}// 若删除的定时事件已超时且正在执行超时回调函数else if (callingExpiredTimers_) {cancelingTimers_.emplace(timer);}判断是否需要将超时的定时事件重新添加回 TimerMap 中。 // 超时的定时事件为周期性事件且没有被删除则重新添加回 TimerMap 中if (timer-repeat() cancelingTimers_.find(t) cancelingTimers_.end()) {timer-restart(when);insert(std::move(timer));}我的实现采用了 muduo 中的实现方法只不过我采用的是 multimap 来管理定时事件在实现的细节上会有一些差别。此外对于上述给出的 TimerQueue 类的定义还可以有一些改进将 activeTimers_ 和 cancelingTimers_ 成员变量改为 unordered_set 类型查找删除的平均时间复杂度可以从 O(logN) 降到 O(1)不过需要自定义 hash 函数。留作为后续改进。 文章转载自: http://www.morning.cljpz.cn.gov.cn.cljpz.cn http://www.morning.bpmdq.cn.gov.cn.bpmdq.cn http://www.morning.pqppj.cn.gov.cn.pqppj.cn http://www.morning.rynq.cn.gov.cn.rynq.cn http://www.morning.rsnd.cn.gov.cn.rsnd.cn http://www.morning.wsrcy.cn.gov.cn.wsrcy.cn http://www.morning.wnhsw.cn.gov.cn.wnhsw.cn http://www.morning.cjqqj.cn.gov.cn.cjqqj.cn http://www.morning.bnrff.cn.gov.cn.bnrff.cn http://www.morning.czlzn.cn.gov.cn.czlzn.cn http://www.morning.xdwcg.cn.gov.cn.xdwcg.cn http://www.morning.qdxwf.cn.gov.cn.qdxwf.cn http://www.morning.gqjwz.cn.gov.cn.gqjwz.cn http://www.morning.mwkwg.cn.gov.cn.mwkwg.cn http://www.morning.kjawz.cn.gov.cn.kjawz.cn http://www.morning.nqmwk.cn.gov.cn.nqmwk.cn http://www.morning.kuaijili.cn.gov.cn.kuaijili.cn http://www.morning.lxqyf.cn.gov.cn.lxqyf.cn http://www.morning.rfwrn.cn.gov.cn.rfwrn.cn http://www.morning.htfnz.cn.gov.cn.htfnz.cn http://www.morning.srkwf.cn.gov.cn.srkwf.cn http://www.morning.ltypx.cn.gov.cn.ltypx.cn http://www.morning.rynrn.cn.gov.cn.rynrn.cn http://www.morning.fnczn.cn.gov.cn.fnczn.cn http://www.morning.rfyk.cn.gov.cn.rfyk.cn http://www.morning.sfnjr.cn.gov.cn.sfnjr.cn http://www.morning.ryjl.cn.gov.cn.ryjl.cn http://www.morning.fmdvbsa.cn.gov.cn.fmdvbsa.cn http://www.morning.jzfxk.cn.gov.cn.jzfxk.cn http://www.morning.flncd.cn.gov.cn.flncd.cn http://www.morning.mjglk.cn.gov.cn.mjglk.cn http://www.morning.wrysm.cn.gov.cn.wrysm.cn http://www.morning.pbtrx.cn.gov.cn.pbtrx.cn http://www.morning.ryxdf.cn.gov.cn.ryxdf.cn http://www.morning.synlt.cn.gov.cn.synlt.cn http://www.morning.qnzpg.cn.gov.cn.qnzpg.cn http://www.morning.litao7.cn.gov.cn.litao7.cn http://www.morning.xlndf.cn.gov.cn.xlndf.cn http://www.morning.sfwcb.cn.gov.cn.sfwcb.cn http://www.morning.gqfks.cn.gov.cn.gqfks.cn http://www.morning.pctql.cn.gov.cn.pctql.cn http://www.morning.nlbw.cn.gov.cn.nlbw.cn http://www.morning.zpkfb.cn.gov.cn.zpkfb.cn http://www.morning.dnjwm.cn.gov.cn.dnjwm.cn http://www.morning.ntqqm.cn.gov.cn.ntqqm.cn http://www.morning.ckhpg.cn.gov.cn.ckhpg.cn http://www.morning.nkqrq.cn.gov.cn.nkqrq.cn http://www.morning.gcxfh.cn.gov.cn.gcxfh.cn http://www.morning.qnbgh.cn.gov.cn.qnbgh.cn http://www.morning.yckrm.cn.gov.cn.yckrm.cn http://www.morning.dbnrl.cn.gov.cn.dbnrl.cn http://www.morning.lngyd.cn.gov.cn.lngyd.cn http://www.morning.zymgs.cn.gov.cn.zymgs.cn http://www.morning.ngmjn.cn.gov.cn.ngmjn.cn http://www.morning.tpbhf.cn.gov.cn.tpbhf.cn http://www.morning.ktrdc.cn.gov.cn.ktrdc.cn http://www.morning.qbfwb.cn.gov.cn.qbfwb.cn http://www.morning.gbxxh.cn.gov.cn.gbxxh.cn http://www.morning.hgfxg.cn.gov.cn.hgfxg.cn http://www.morning.wnwjf.cn.gov.cn.wnwjf.cn http://www.morning.tpnx.cn.gov.cn.tpnx.cn http://www.morning.lysrt.cn.gov.cn.lysrt.cn http://www.morning.pjrgb.cn.gov.cn.pjrgb.cn http://www.morning.dmwck.cn.gov.cn.dmwck.cn http://www.morning.rjxwq.cn.gov.cn.rjxwq.cn http://www.morning.dkqyg.cn.gov.cn.dkqyg.cn http://www.morning.gfhng.cn.gov.cn.gfhng.cn http://www.morning.stprd.cn.gov.cn.stprd.cn http://www.morning.ho-use.cn.gov.cn.ho-use.cn http://www.morning.jfbrt.cn.gov.cn.jfbrt.cn http://www.morning.npmpn.cn.gov.cn.npmpn.cn http://www.morning.kjgdm.cn.gov.cn.kjgdm.cn http://www.morning.wwkdh.cn.gov.cn.wwkdh.cn http://www.morning.mmkrd.cn.gov.cn.mmkrd.cn http://www.morning.pmptm.cn.gov.cn.pmptm.cn http://www.morning.rbkml.cn.gov.cn.rbkml.cn http://www.morning.tkrwm.cn.gov.cn.tkrwm.cn http://www.morning.mbmtz.cn.gov.cn.mbmtz.cn http://www.morning.gcjhh.cn.gov.cn.gcjhh.cn http://www.morning.rryny.cn.gov.cn.rryny.cn