范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

高性能定时器策略之时间轮定时器算法

  时间轮定时器是有多个时间槽(slot )组成,每个时间槽代表时间的基本跨度。类似于一个时钟,时钟指针以恒定的速度每往下走一步,代表一个时间跨度。
  时间槽的个数是固定的,用SN (Slot Num) 表示,每转动一次可称为一个滴答(tick),所以转动一周也就需要个SN 滴答。每一个滴答需要的槽间间隔为SI (Slot Interval ),则转一周也就共需要(SN * SI )时间单位。
  时间轮的每个槽都指向一个定时器链表,每个链表的定时器都有一个相同的特性:相差(SN * SI )的整数倍,也即是一周的整数倍。正是由于时间轮有这样的特性,所以可以根据定时器的触发时间和时间轮的槽数,把定时器hash到对应的链表上。
  一个简单的时间轮如下图:
  比如我们要插入一个超时时间为TI 的定时器,那么计算器应该插入到槽对应的链表的方式如下:
  TS = ( CS + (TI / SI) ) % SN
  CS 代表当前的槽位,因为时间轮定时器不停的在走。
  时间轮采用的是hash的思想,把各个定时器分散到不同槽的链表中,这个避免了单个链表的过长,提高了效率。
  从上面的公式可以看出,对时间轮而言,当SI  越小的时候,定时器的精度就越高;当SN 槽数越多时,效率越高,因为定时器被分配到不同的链表中,链表的长度也就相对短了,提高了遍历效率。
  下面实现一个简单的时间轮,代码如下:
  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include    #define BUFFER_SIZE  64 //缓冲区长度 #define SLOTS_NUM    60 //时间轮上槽的数目   //结构体声明 typedef struct tw_timer tw_timer; typedef struct client_date client_date;   /* EPOLL回调函数*/ typedef void (*epoll_ callback_ t)(int, int);    struct tw_timer{     int rotation; //记录定时器在时间轮上转多少圈后触发     int time_slot; //记录定时器属于时间轮上的槽位(对应的链表)     void (*cb_func)(client_date *); //定时器回调函数     client_date *user_date; //client数据     tw_timer *prev;  //指向前一个定时器     tw_timer *next ;  //指向下一个定时器 }; //用户数据  struct client_date{     int sockfd;      char buf[BUFFER_SIZE];     tw_timer *time;  };   //每1秒时间轮转动一次,也即是槽间隔为1秒 static const int SI = 1; //时间轮的槽, 其中每个元素指向一个定时器链表,链表无序 tw_timer *slots[SLOTS_NUM];    //事件的当前槽 int cur_slot = 0;   tw_timer * malloc_tw_timer(int rotation, int time_slot) {     tw_timer *timer = (tw timer *)malloc(sizeof(tw_timer));      memset(timer, 0, sizeof(tw_timer));     timer->rotation = rotation;     timer->time_slot = time_slot;     timer->prev = NULL ;     timer->next = NULL ;     timer->cb_func = NULL ;     timer->user_date = NULL;     return timer;   }   void* free_tw_timer(tw_timer *timer) {     if(NULL == timer)         return NULL;          if(NULL != timer->user_date)         free(timer->user_date);          free(timer); }    void time_wheel() {     int i = 0;     for(i = 0; i < SLOTS_NUM; i++)       slots[i] = NULL; }   //遍历每个槽,销毁定时器 void del_time_wheel() {     int i = 0;     for(i = 0; i < SLOTS_NUM; i++)     {     tw_timer *tmp = slots[i];     while(tmp)     {       slots[i] = tmp->next;       free_tw_timer(tmp);       tmp = slots[i];     }         } }    /*根据定时器timeout创建一 个定时器,并把它插入到一个合适的槽中*/  tw_timer * add_timer(int timeout) {     if(timeout < 0)         return NULL;       int ticks = 0;     /*待插入的超时时间小于时间轮的槽间隔SI,则将ticks向 上调整为1,       否则将ticks向下调整为 timeout/SI      */     if(timeout < SI)      {     ticks = 1;     }     else     {         ticks = timeout / SI;     }       //计算待插入的定时器在时间轮转动多少圈后被触发     int rotation = ticks / SLOTS_NUM;     //计算待插入的定时器应该被插入到哪个槽中     int ts = (cur_slot + (ticks % SLOTS_NUM)) % SLOTS_NUM;     //创建新的定时器,它在时间轮转动rotation圈后被触发     tw_timer *timer = malloc_tw_timer(rotation, ts);     /*若第ts个槽为空,则插入定时器,并将该定时器置为该槽的头结点*/     if(!slots[ts])     {         printf("add timer, rotation is %d, ts is %d, cur_ slot is %d ",                  rotation, ts, cur_slot);          slots[ts] = timer;     }     else //否则,将定时器插入其中     {         printf("slots[%d] is not null ", ts);      timer->next = slots[ts];         slots[ts]->prev = timer;         slots[ts] = timer;     }        return timer; }   void del_timer(tw_timer *timer) {     if(!timer)       return ;          int ts = timer->time_slot;       /*slots[ts]是目标定时器所在槽头结点,若目标定时器为头结点,则需要重置第ts个槽的头结点*/     if(timer == slots[ts])    {     slots[ts] = slots[ts]->next;         if(slots[ts])         {       slots[ts]->prev = NULL;       }      }     else     {     timer->prev->next = timer->next;     if(timer->next)     {         timer->next->prev = timer->prev;     }            }      free_tw_timer(timer);     }   //SI 时间到后,调用该函数, 时间轮向前滚动一个槽的间隔 void tick() {   tw_timer *tmp = slots[cur_slot];   //遍历该槽对应链表的所有定时器,查看是否触发   while(tmp)   {     if(tmp->rotation > 0)     {       tmp->rotation--;       tmp = tmp->next;     }     else //到期,执行任务     {       tmp->cb_func(tmp->user_date);       if(tmp == slots[cur_slot])       {         printf("delete head in cur_slot, cur_slot %d ", cur_slot);         slots[cur_slot] = tmp->next;         free_tw_timer(tmp);                  if(slots[cur_slot])           slots[cur_slot]->prev = NULL;                    tmp = slots[cur_slot];       }       else       {         tmp->prev->next = tmp->next;         if(tmp->next)         {           tmp->next->prev = tmp->prev;         }                tw_timer *tmp2 = tmp->next;         free_tw_timer(tmp);                 tmp = tmp2;       }          }   }     //更新时间轮的当前槽,以反映时间轮的转数   cur_slot = ++cur_slot % SLOTS_NUM; }
  测试如下  int total = 0; //记录1s定时器触发次数   void test (client_date *cd) {    printf("total = %d ", total); }   void test_add_timer(int timeout, void (*cb_func)(client_date *)) {     tw_timer * timer;     timer = add_timer(timeout);      timer->cb_func = cb_func;   }     void test_crete_timer() {     //创建一一个2秒5秒10秒70秒, 85秒的定时器      test_add_timer(2, test);     test_add_timer(5, test);     test_add_timer(10, test);     test_add_timer(70, test);     test_add_timer(85, test);        time_t now ;     struct tm *tm_now ;     time(&now) ;     tm_now = localtime(&now) ;     printf("test_crete_timer datetime: %d-%d-%d %d:%d:%d ", tm_now->tm_year+1900,          tm_now->tm_mon+1, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);    }   //保存触发定时器相应的fd和回调,在该回调中进行遍历时间轮 typedef struct epoll_callback_info {     int iFd;     epoll_callback_t pfEpollCallBack;  }epoll_callback_info;   //创建一个触发定时器 int createTimer(long lMSec, int epfd, epoll_callback_t pfEpollCallback) {   struct itimerspece stTimer;   struct epoll_event event;   int timerfd;   int iSec = (int)(lMSec/1000);   int ret = -1;     memset(&event, 0, sizeof(event));   event.events = EPOLLIN | EPOLLHUP | EPOLLERR;      memset(&stTimer, 0, sizeof(stTimer));   stTimer.it_value.tv_sec = iSec;   stTimer.it_value.tv_nsec = (lMSec%1000)*1000000;   stTimer.it_interval.tv_sec = iSec;   stTimer.it_interval.tv_nsec = (lMSec%1000)*1000000;      timerfd = timerfd_create(CLOCK_MONOTONIC, 0);   if(-1 != timerfd)   {     if(0 == timerfd_settime(timerfd, 0, &stTimer, NULL))     {       epoll_callback_info *p = malloc(sizeof(epoll_callback_info));       p->iFd = timerfd;       p->pfEpollCallback = pfEpollCallback;              event.data.ptr = p;       ret = epoll_ctl(epfd, EPOLL_CTL_ADD, timerfd, &event);     }     else     {       printf("timerfd_settime Failed");     }          /*定时器时间设定或添加epoll错误时,释放资源*/     if(0 != ret)     {       printf("add to epoll failed or set time failed");       close(timerfd);       timerfd = -1;     }   }   else   {     printf("create timerfd failed");   }     return timerfd; }   void timer_read_timerFd(int timerfd) {   uint64_t exp;   (void)read(timerfd, &exp, sizeof(uint64_t));      total++;   return; }   //epoll上定时器回调,每触发一次则调用一次tick() void commomTimerCB(int uiEvent, int iTimeFd) {   timer_read_timerFd(iTimeFd);   tick(); }     int main() {   struct epoll_event wait_event[100];   int epfd;   int timerfd;   epoll_callback_t pfEpllCallback;   int i = 0;   struct itimerspece stTimer;   long lMSec = 1000; //1s定时器时间   int count;      epfd = epoll_create(1);   if(-1 == epfd)   {     printf("epoll create error ");     return -1;   }      printf("create epoll fd: %d ", epfd);   time_wheel();   test_create_timer();      timerfd = createTimer(lMSec, epfd, (epoll_callback_t)commomTimerCB);   if(-1 == timerfd)   {     printf("failed to create timer");     return -1;   }      for(;;)   {     waitfds = epoll_wait(epfd, wait_event, 100, -1);     printf("count %d ", count++);          for(i = 0; i < waitfds; i++)     {       epoll_callback_info *t = (epoll_callback_info*)wait_event[i].data.ptr;       pfEpllCallback = (epoll_callback_t)(unsigned long)t->pfEpllCallback;       pfEpllCallback(wait_event[i].events, t->iFd);     }   }      return 0; }

金价跌了!2022年10月14日各大金店黄金价格多少钱一克?周五(10月14日),国际金价小幅走高,受美元和美债收益率回落提振。而国内黄金价格出现了微跌,已经有4家金店黄金报价低于500元了,周生生和老庙黄金均报价497元克,中国黄金报价最化肥价格高位回落非肥业务或成企业下半年亮点10月13日,化肥龙头企业藏格矿业(000408)湖北宜化(000422)同日披露前三季度业绩预告。两家公司前三季度业绩增幅亮眼藏格矿业前三季度净利润预计增幅为401至413,湖北中企通信亮相2022(第十三届)IT运维大会9月8日,由IT运维网网络安全和信息化杂志社联合主办的2022(第十三届)IT运维大会在北京成功举办。中企通信作为国内较早从事ICT服务商,受主办方邀参与此次活动,信息科技服务及数爱驰U6正式发布,轿跑与SUV的结合体说起新能源车型,相信大家还是比较感兴趣的,近几年国内的新能源市场可以说是鱼龙混杂,各类品牌各种车型相继推出自家的新能源车型,可以说是非常热闹,不过最后剩下的才是王者,今天介绍的这款慢性肾病如何管理好饮食?坚持这一点,肾功能更稳定管理好饮食对肾病的治疗效果有很大的影响,好的饮食习惯,遵守正确的饮食原则对肾功能的保护能起到关键的作用。肾脏患者在整体上要遵循五低二高的饮食原则。很多患者弄不明白,在这里小编整理了华为nova8SE跌至新低价,2022年,到底值不值得入手呢?随着双十一的即将到来,国产手机厂商纷纷开始了新一轮的促销活动。笔者发现国产手机老大哥华为手机,对旗舰机的价格做出了很大的调整。其中,华为nova8SE的价格更是跌至了新低价。价格来微信又有新功能微信又上线新功能近日微信上线微信刷掌支付相关话题冲上热搜据中国经济周刊,近日,微信内已上线微信刷掌支付小程序,可为用户更加便捷的管理刷掌支付,但刷掌支付功能需要在刷掌设备中开通。刷2021专利排行榜出炉啦!OPPO居中国企业专利排名第二你第一次认识OPPO是什么时候?源于一台MP3?还是一部微笑手机?又或者是一条充电5分钟,通话两小时的广告?在过去的18年里,为给大家带来更好的产品,OPPO成立了专业的研发团队。老美打开潘多拉魔盒,英特尔遭反噬,比尔盖茨预言成真两年前,比尔盖茨曾在接受媒体采访时表示,如果继续利用芯片对中企进行极限施压,加速中美企业在科技领域的脱钩,不但不会遏制中国科技的崛起,反而会让美企遭受难以想象的损失。对于比尔盖茨的满世界找气的欧洲人都在抱团取暖?错,天然气多到超出你想象北溪被炸之后,前天,波兰一条极其重要的石油管道出现泄漏,昨天,挪威的一家天然气工厂遭到电话威胁紧急疏散。每一次事故,都会刺激天然气价格巨幅波动。背后到底有多少人为操纵的因素,还真不新规出台,网约车纳入客运出租管理,运价要被监管了新规出台,网约车纳入客运出租车管理,网约车运价终于要被监管了!10月12日上午,市委市政府新闻发布会召开,对济南市客运出租汽车管理条例(以下简称条例)进行解读,新条例将于12月1日
生活中的辩证法有时候,失望到一定程度后,反而会开出一朵花来,那朵花的名字叫,无所谓。每一个闪闪发光的人,都在背后熬过了一个又一个不为人知的黑夜,那才是真正值得我们拥有和赞叹的地方。人生中有无数的我们总避免不了失望和失去,要学会自己哄自己工作丢了可以再找,钱没了可以再挣,朋友走了可以再交,记住,你什么都不缺,你缺的是一份重新开始的勇气。Ifyouloseyourjob,youcanfinditagain,ifyou往事不言愁,余生不悲秋人世浮沉,繁华落尽,前尘往事,终会成为故事。寻寻觅觅,总会到达终点。曾经再让人留恋,也已成过眼云烟,昨天再美好,也无法重头再来。无论昨日是美好还是悲伤,都不再重要了,重要的是把握好图集绿水青山惹人醉大美梅县迎客来绿水青山生态美,景色如画引客来。图为桥溪古韵景区。王志成摄漫步桥溪古韵沉浸天然氧吧,畅玩水美村体验农家乐趣,亲子出游雁山湖打卡粉色沙滩国庆假期,梅县区优质的生态环境和旅游产品供给,连山道市场时空论(二)国际市场的考察国际市场考察三个层面孙子曰知己知彼,百战不殆。中国企业对国际市场的研究,对各个洲各个国家各个地区市场的全面考察衡量,可以从经济地理社会三个层面进行。1。经济层面包括经济发展水平人口美股开盘道指涨近150点中概股多下跌爱奇艺B站跌超7金融界10月10日消息,因地缘政治紧张局势以及市场担心美联储可能继续采取激进的货币政策行动,市场避险情绪持续升温,美股微幅高开,道指涨近150点,默沙东涨2。7,Rivian跌6。重庆市到2025年建成充电桩超24万个来源盖世汽车向天歌日前,重庆市发布重庆市推进智能网联新能源汽车基础设施建设及服务行动计划(20222025年),提及到2025年全市要建成充电桩超过24万个。重庆表示,到2025年NFR是否比NFT更适应本土生长NFT这个词,大家应该已经有深刻的认识了,全称为NonFungibleToken,指非同质化通证,实质是区块链网络里具有唯一性特点的可信数字权益凭证,是一种可在区块链上记录和处理多教女心得,家庭环境塑造人,用努力学习给她做表率,榜样的力量一直以来在怎么教育孩子这个问题上没有完整的体系,纵然是各种网络上的鸡汤,能够形成各自的人还是少之又少,当然有很多鸡汤根本就是专家的纸上谈兵,白白制造了无数家长的焦虑。我个人始终深信睡觉出汗的孩子,可不仅仅是因为热了,家长要了解小孩子出现睡觉大汗淋漓的情况被称为盗汗,对于五岁以下的孩子来说,这种情况相对多见,因为孩子此刻由于身体生长旺盛,而排汗功能还在完善建立过程当中,故而汗液多。虽然孩子的盗汗,大致被医家长必看的正面管教手册!5种错误惩罚方式和5种科学惩罚方式孩子犯了错,要不要惩罚?答案是要。只是说教的话,孩子根本不长记性啊!但惩罚不是目的,孩子们需要规则,也需要尊重。作为两个娃的爸爸,萌医生非常理解,很多时候我们并不是真的想要打骂孩子