基于epoll的定时器

先上代码

event_poll.h(代码不多,就不分cpp了)

#pragma once

#include <assert.h>
#include <memory.h>
#include <sys/epoll.h>
#include <unistd.h>

#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>

using namespace std;

class EventPoll {

  struct EpollUserData {
    uint32_t event_type;      //关注的事件类型
    function<void(int)> fun;  //回调函数
  };

 public:
  EventPoll(/* args */) : stop_(true) { epoll_fd_ = epoll_create1(0); };
  ~EventPoll() {
    for (auto data : user_data_) {
      EraseTimer(data.first);
    }
    close(epoll_fd_);
  };
  //添加定时器
  int AddTimer(function<void(void)> fun, float interval) {
    int fd = CreatTimerfd(interval);
    std::lock_guard<std::mutex> guard(mutex_);
    user_data_[fd] = {EPOLLIN, [fun](int fd) {
                        fun();
                        uint64_t time_now;
                        read(fd, &time_now, sizeof(time_now));
                      }};
    AddEvent(fd, EPOLLIN);
    return fd;
  }
  //擦除定时器
  void EraseTimer(int fd) {
    std::lock_guard<std::mutex> guard(mutex_);
    epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr);
    if (user_data_.count(fd) != 0) {
      user_data_.erase(fd);
    }
    close(fd);
  }
  //开始epoll
  void run_epoll() {
    stop_ = false;
    epoll_thread_ = std::thread(&EventPoll::run, this);
  }
  void stop_poll() {
    stop_ = true;
    epoll_thread_.join();
  };

 private:
  bool stop_;                                         //停止标志值
  int epoll_fd_;                                      // epoll的fd
  std::mutex mutex_;                                  //数据锁
  std::thread epoll_thread_;                          // epoll线程池
  std::unordered_map<int, EpollUserData> user_data_;  //用户数据
  //创建定时器fd
  int CreatTimerfd(float interval) {
    int fd = timerfd_create(CLOCK_REALTIME, 0);
    timespec current_time;
    clock_gettime(CLOCK_REALTIME, &current_time);
    itimerspec timer_spec;
    timer_spec.it_value = current_time;
    timer_spec.it_interval = {int(interval),
                              int((int(1000 * interval) % 1000) * 1e6)};
    timerfd_settime(fd, TFD_TIMER_ABSTIME, &timer_spec, nullptr);
    return fd;
  }
  //添加事件
  void AddEvent(int fd, int event_type) {
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event);
  }
  // epoll主循环
  void run() {
    std::vector<epoll_event> revents(1024);
    while (!stop_) {
      int res = epoll_wait(epoll_fd_, revents.data(), 1024, 100);
      if (res > 0) {
        std::lock_guard<std::mutex> guard(mutex_);
        for (int i = 0; i < res; ++i) {
          if (user_data_.count(revents[i].data.fd) == 0) {
            // error
            continue;
          }
          if (revents[i].events & user_data_[revents[i].data.fd].event_type) {
            user_data_[revents[i].data.fd].fun(revents[i].data.fd);
          }
        }
      }
    }
  }
};

 

测试程序(运行环境:ubuntu20,g++9)

void time_print(int timer_id) {
  timespec current_time;
  clock_gettime(CLOCK_REALTIME, &current_time);
  cout << "timer" << timer_id << " : " << current_time.tv_sec << "s , "
       << int(current_time.tv_nsec / 1e6) << "ms" << endl;
}

int main() {
  cout << "start" << endl;
  EventPoll epoll;
  int fd1 = epoll.AddTimer(std::bind(time_print, 1), 1);
  int fd2 = epoll.AddTimer(std::bind(time_print, 2), 2);
  epoll.run_epoll();
  std::this_thread::sleep_for(std::chrono::milliseconds(10000));
  epoll.EraseTimer(fd1);
  std::this_thread::sleep_for(std::chrono::milliseconds(5000));
  epoll.stop_poll();
  cout << "end" << endl;
  return 0;
}

 

几个重要的点:

epoll:

1、结构体epoll_event内的epoll_data_t数据是一个联合体,不要考虑填了void*又填fd。我是考虑只填fd然后在触发的时候根据fd找到对应的回调信息,你也可以把数据结构打包给void*。如果你选择了后者,请管理好指针所指对象的内存。

2、epoll_ctl的输入参数epoll_event*,可能你会误会它让你保管指针数据,其实不用管,你做添加事件的时候它内部会复制一份数据的,你做删除的时候不用传入数据,填nullptr就好。

3、epoll_create1比epoll_create好用,填0它会自己根据事件多少进行扩展。

 

posted @ 2022-07-06 18:01  永驻的青春  阅读(946)  评论(0)    收藏  举报