定时器

定时事件被封装为一个定时器对象,定时器被容纳到定时器容器内。二者通常混谈。这里讨论两种高效的管理定时器的容器:时间轮和时间堆

实现方式

  1. 组织方式
    1. 时间序
      1. 红黑树
        nginx
      2. 最小堆
        libevent libev go语言
    2. 执行序
      1. 时间轮
        netty skynet kafka linux内核
  2. 应用方式
    1. 单线程
      多见红黑树或最小堆
    2. 多线程
      多见时间轮

定时器与其他模块关系

与网络模块协同处理,基于事件驱动业务的开展。
除了系统网络处理,还可以复用系统调用。如epoll的超时事件。

定时器基础接口设计

  1. 最近触发任务离当前还有多久
  2. 获取当前时间的接口
    c++11 提供 #include <chrono>
    steady_clock(系统启动到现在的时间,可用于计算程序运行时间)
    system_clock(时间戳,可以修改)
    high_resolution_clock(高精度版本 steady_clock)
  3. 添加定时器
  4. 删除定时器
  5. 检查定时器

实现

使用Linux提供的具有超时功能的函数

Linux 提供了3种定时方法:

  1. socket 选项的 SO_RCVTIMEO(接收数据超时时间) 和 SO_SNDTIMEO(发送数据超时时间)
    这两个选项堆socket专用api有效,如 send sendmsg recv recvmsg accept connect
  2. SIGALRM 信号
  3. IO 复用系统调用的超时参数
    例如 epoll 的超时参数

 

SIGALRM 信号

使用 alarm 和 setitimer 函数设置实时闹钟,每隔超时周期的整数倍后就发出SIGALRM信号。

代码
 #ifndef LST_TIMER
#define LST_TIMER

#include <arpa/inet.h>
#include <sys/socket.h>
#include <time.h>

#include <iostream>

#define BUFFER_SIZE 64

class util_timer;

/// @brief 用户数据结构
struct client_data {
  sockaddr_in address;
  int sockfd;
  char buf[BUFFER_SIZE];
  util_timer* timer;
};

/// @brief 定时器类
class util_timer {
 public:
  time_t expire;                  // 超时时间,这里使用绝对时间
  void (*cb_func)(client_data*);  // 任务回调函数
  // 回调函数处理的客户数据,由定时器的执行者传递给回调函数
  client_data* user_data;
  util_timer* prev;
  util_timer* next;

  util_timer() : prev(nullptr), next(nullptr){};
  ~util_timer(){};
};

/*****************************************************/

/// @brief 定时器列表,它是一个升序、双向链表,且带有头节点和尾节点
class sort_timer_lst {
 private:
  util_timer* head;
  util_timer* tail;

 public:
  sort_timer_lst() : head(nullptr), tail(nullptr){};

  /// @brief 定时器添加到列表
  void add_timer(util_timer* timer);

  /// @brief
  /// 定时任务发生时,调整对应链表的位置。此处只考虑超时时间延长的情况,即节点只向尾部移动
  void adjust_timer(util_timer* timer);

  /// @brief 将目标从定时器删除
  void del_timer(util_timer* timer);

  /// @brief SIGALRM
  /// 信号每次被触发就在其信号处理函数(如果使用同一信号源,则是主函数)种执行一次
  /// tick 函数,以处理链表上到期任务
  void tick();
  ~sort_timer_lst(){};

 private:
  /// @brief 一个重载的辅助函数,被 add_timer 和 adjust_timer
  /// 调用。该函数将目标定时器timer添加到节点lst_head之后的部分链表的合适位置
  void add_timer(util_timer* timer, util_timer* lst_head);
};

void sort_timer_lst::add_timer(util_timer* timer) {
  if (!timer) {
    return;
  }
  if (!head) {
    head = tail = timer;
    return;
  }
  // 根据超时时间将节点放入合适的位置
  if (timer->expire < head->expire) {
    timer->next = head;
    head->prev = timer;
    head = timer;
    return;
  }
  add_timer(timer, head);
}

void sort_timer_lst::adjust_timer(util_timer* timer) {
  if (!timer) {
    return;
  }
  util_timer* tmp = timer->next;
  if (!tmp || (timer->expire < tmp->expire)) {
    return;
  }
  if (timer == head) {
    head = head->next;
    head->prev = nullptr;
    timer->next = nullptr;
    add_timer(timer, head);
  } else {
    timer->prev->next = timer->next;
    timer->next->prev = timer->prev;
    add_timer(timer, timer->next);
  }
}

void sort_timer_lst::del_timer(util_timer* timer) {
  if (!timer) {
    return;
  }
  // 链表中只有一个定时器
  if ((timer == head) && (timer == tail)) {
    delete timer;
    head = nullptr;
    tail = nullptr;
    return;
  }
  // 链表有多个定时器,且定时器为链表头节点,则重置头节点再删除
  if (timer == head) {
    head = head->next;
    head->prev = nullptr;
    delete timer;
    return;
  }
  // 链表有多个定时器,且定时器为链表尾节点,则重置尾节点再删除
  if (timer == tail) {
    tail = tail->prev;
    tail->next = nullptr;
    delete timer;
    return;
  }
  // 定时器位于中间
  timer->prev->next = timer->next;
  timer->next->prev = timer->prev;
  delete timer;
}

void sort_timer_lst::tick() {
  if (!head) {
    return;
  }
  std::cout << "timer tick" << std::endl;
  time_t cur = time(nullptr);  // 获得当前时间
  util_timer* tmp = head;
  // 从头开始处理定时器,直到遇到未到期的定时器
  while (tmp) {
    if (cur < tmp->expire) {
      break;
    }
    tmp->cb_func(tmp->user_data);
    head = tmp->next;
    if (head) {
      head->prev = nullptr;
    }
    delete tmp;
    tmp = head;
  }
}

void sort_timer_lst::add_timer(util_timer* timer, util_timer* lst_head) {
  util_timer* prev = lst_head;
  util_timer* tmp = prev->next;
  while (tmp) {
    if (timer->expire < tmp->expire) {
      prev->next = timer;
      timer->next = tmp;
      tmp->prev = timer;
      timer->prev = prev;
      break;
    }
    prev = tmp;
    tmp = tmp->next;
  }
  if (!tmp) {
    prev->next = timer;
    timer->prev = prev;
    timer->next = nullptr;
    tail = timer;
  }
}

sort_timer_lst::~sort_timer_lst() {
  util_timer* tmp = head;
  while (tmp) {
    head = tmp->next;
    delete tmp;
    tmp = head;
  }
}

#endif  // LST_TIMER

tick函数每隔一段固定时间就执行一次。

基于epoll超时功能的定时器

使用set结构底层的红黑树实现

epoll RBTree 定时器
 #include <sys/epoll.h>

#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
#include <set>

// 此处使用继承可以避免func对象被反复复制
struct TimerNode {
  using Callback = std::function<void(const TimerNode &node)>;
  Callback func;  // 定时任务被触发时执行的回调方法
  time_t expire;
  int64_t id;
  time_t gap;  // 重复执行的间隔,为0时不重复执行
};

// 利用多态
bool operator<(const TimerNode &l, const TimerNode &r) {
  if (l.expire < r.expire) {
    return true;
  }
  if (l.expire > r.expire) {
    return false;
  }
  return l.id < r.id;
}

class Timer {
 public:
  static time_t GetTick() {
    auto sc = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::steady_clock::now());
    auto temp = std::chrono::duration_cast<std::chrono::milliseconds>(
        sc.time_since_epoch());
    return temp.count();
  }

  static int64_t GenID() { return ++gid; }

  TimerNode AddTimer(time_t msec, TimerNode::Callback func, time_t gap = 0) {
    TimerNode tnode;
    tnode.expire = GetTick() + msec;
    tnode.func = func;
    tnode.id = GenID();
    tnode.gap = gap;
    // 加到某个数据结构中
    this->timermap.insert(tnode);
    // std::cout << "create new TimerNode : " << tnode.expire << " " << tnode.id
    // << std::endl;
    return static_cast<TimerNode>(tnode);
  }

  bool DelTimer(TimerNode &node) {
    auto iter = this->timermap.find(node);
    if (iter != this->timermap.end()) {
      this->timermap.erase(iter);
      return true;
    }
    return false;
  }

  bool checkTimer() {
    // 先找到某个数据结构最小的节点
    auto iter = this->timermap.begin();
    if (iter != this->timermap.end() && iter->expire <= GetTick()) {
      iter->func(*iter);
      this->timermap.erase(iter);
      if (iter->gap != 0) {
        this->AddTimer(iter->gap, iter->func, iter->gap);
      }
      return true;
    }
    // 没有任务可以执行时返回 false
    return false;
  }

  time_t TimeToSleep() {
    auto iter = this->timermap.begin();
    if (iter == this->timermap.end()) {
      return -1;
    }
    time_t diss = iter->expire - GetTick();
    // std::cout << "diss : " << diss << " " << iter->expire << " " << GetTick()
    //           << std::endl;
    return diss > 0 ? diss : 0;
  }

 private:
  static int64_t gid;
  std::set<TimerNode, std::less<>> timermap;
};
int64_t Timer::gid = 0;

int main(int argc, char const *argv[]) {
  std::unique_ptr<Timer> timer = std::make_unique<Timer>();

  int i = 0;
  timer->AddTimer(
      1000,
      [&i](const TimerNode &node) {
        std::cout << Timer::GetTick() << " "
                  << "1 node id: " << node.id << " " << ++i << std::endl;
      },
      1000);
  timer->AddTimer(1000, [&i](const TimerNode &node) {
    std::cout << Timer::GetTick() << " "
              << "2 node id: " << node.id << " " << ++i << std::endl;
  });
  timer->AddTimer(3000, [&i](const TimerNode &node) {
    std::cout << Timer::GetTick() << " "
              << "3 node id: " << node.id << " " << ++i << std::endl;
  });

  auto node = timer->AddTimer(3000, [&i](const TimerNode &node) {
    std::cout << Timer::GetTick() << " "
              << "deleted node id: " << node.id << " " << ++i << std::endl;
  });
  if (timer->DelTimer(node)) {
    std::cout << "success" << std::endl;
  } else {
    std::cout << "fail" << std::endl;
  }

  std::cout << "current time: " << timer->GetTick() << std::endl;
  int epfd = epoll_create(1);

  epoll_event events[64] = {0};

  while (true) {
    epoll_wait(epfd, events, 64, timer->TimeToSleep());
    // for (int i = 0; i < 64; ++i) {}
    timer->checkTimer();
  }

  return 0;
}

 

原文地址:http://www.cnblogs.com/zhh567/p/16798576.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性