基于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, ¤t_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, ¤t_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它会自己根据事件多少进行扩展。