zoukankan      html  css  js  c++  java
  • Redis事件管理(二)

    Redis的定时器是自己实现的,不是很复杂。说说具体的实现吧。

    定时器的存储维护采用的是普通的单向链表结构,具体节点定义为:

     1 /*时间定时器结构体*/
     2 typedef struct aeTimeEvent 
     3 {
     4     long long id; /*定时器的编号*/
     5     long when_sec; /* seconds */
     6     long when_ms; /* milliseconds */
     7     aeTimeProc *timeProc;/*时间到达处理函数*/
     8     aeEventFinalizerProc *finalizerProc;/*删除清理函数*/
     9     void *clientData;/*带外数据*/
    10     struct aeTimeEvent *next;/*定时器的存储采用的是链表结构*/
    11 } aeTimeEvent;

    定时器记录的根节点的位置是在事件管理器中,剩下的就是普通的操作函数了

     1 /*添加定时器事件,参数为时间控制器,定时时间,处理函数,函数参数,清理函数*/
     2 long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
     3         aeTimeProc *proc, void *clientData,
     4         aeEventFinalizerProc *finalizerProc)
     5 {
     6     /*生成ID*/
     7     long long id = eventLoop->timeEventNextId++;
     8     aeTimeEvent *te;
     9 
    10     te = zmalloc(sizeof(*te));
    11     if (te == NULL) return AE_ERR;
    12     te->id = id;
    13     /*调整时间*/
    14     aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
    15     te->timeProc = proc;
    16     te->finalizerProc = finalizerProc;
    17     te->clientData = clientData;
    18     /*加入时间链表中,所有的时间节点都是在头部插入的,没顺序*/
    19     te->next = eventLoop->timeEventHead;
    20     eventLoop->timeEventHead = te;
    21     return id;
    22 }
     1 /*删除时间节点,提供时间点的ID就好,如果没找到就返回错误*/
     2 int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
     3 {
     4     aeTimeEvent *te, *prev = NULL;
     5 
     6     te = eventLoop->timeEventHead;
     7     /*循环查找节点,调用清理函数并释放内存*/
     8     while(te) 
     9     {
    10         if (te->id == id) 
    11         {
    12             if (prev == NULL)
    13                 eventLoop->timeEventHead = te->next;
    14             else
    15                 prev->next = te->next;
    16             if (te->finalizerProc)
    17                 te->finalizerProc(eventLoop, te->clientData);
    18             zfree(te);
    19             return AE_OK;
    20         }
    21         prev = te;
    22         te = te->next;
    23     }
    24     return AE_ERR; /* NO event with the specified ID found */
    25 }
     1 /*查找激活时间最短的节点*/
     2 static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
     3 {
     4     aeTimeEvent *te = eventLoop->timeEventHead;
     5     aeTimeEvent *nearest = NULL;
     6 
     7     while(te) 
     8     {
     9         if (!nearest || te->when_sec < nearest->when_sec || (te->when_sec == nearest->when_sec && te->when_ms < nearest->when_ms))
    10             nearest = te;
    11         te = te->next;
    12     }
    13     return nearest;
    14 }
     1 /* Process time events 处理时间事件,先执行定时器事件,如果有时间设置过端的情况,会导致整个卡死*/
     2 static int processTimeEvents(aeEventLoop *eventLoop) 
     3 {
     4     int processed = 0;
     5     aeTimeEvent *te;
     6     long long maxId;
     7     time_t now = time(NULL);
     8 
     9     if (now < eventLoop->lastTime) /*为啥会出现当前时间小于上次激活时间的问题,重新设置了系统时间,当前时间小于了最后的插入时间*/
    10     {
    11         te = eventLoop->timeEventHead;
    12         while(te) 
    13         {
    14             te->when_sec = 0;
    15             te = te->next;
    16         }
    17     }
    18     eventLoop->lastTime = now;/*更新最后操作时间*/
    19 
    20     te = eventLoop->timeEventHead;
    21     maxId = eventLoop->timeEventNextId-1;
    22     /*时间处理,从链表中逐个检查,找到到达时间的定时器后调用处理函数,然后重新从头遍历,当某个定时器的*/
    23     while(te) 
    24     {
    25         long now_sec, now_ms;
    26         long long id;
    27 
    28         if (te->id > maxId) 
    29         {
    30             te = te->next;
    31             continue;
    32         }
    33         aeGetTime(&now_sec, &now_ms);
    34         if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms))
    35         {/*当该节点时间到达时,调用时间处理函数,根据函数的返回来决定是否需要删除定时节点*/
    36             int retval;
    37 
    38             id = te->id;
    39             retval = te->timeProc(eventLoop, id, te->clientData);
    40             processed++;
    41 
    42             /*根据函数的返回值来确定是否仍需注册,返回值为下次的激活时间*/
    43             if (retval != AE_NOMORE) 
    44             {
    45                 /*返回值不为-1时需要重新注册函数,返回值为下次激活的时间*/
    46                 aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
    47             } 
    48             else 
    49             {
    50                 /*删除定时器*/
    51                 aeDeleteTimeEvent(eventLoop, id);
    52             }
    53             /*定时器函数处理完毕之后为了防止漏掉节点,重新从头结点遍历,这个地方要注意,如果定时时间太短,这个地方会造成死循环,不停的处理定时器事件*/
    54             te = eventLoop->timeEventHead;
    55         } 
    56         else 
    57         {
    58             te = te->next;
    59         }
    60     }
    61     return processed;
    62 }
  • 相关阅读:
    Redis面试题
    Mysql面试题
    Mybatis面试题
    Springmvc面试题
    spring常见面试题
    优雅的参数校验
    Linux安装mongodb
    Redis缓存的雪崩、穿透、击穿
    语音识别(LSTM+CTC)
    大数据利器Hive
  • 原文地址:https://www.cnblogs.com/likui360/p/5299186.html
Copyright © 2011-2022 走看看