封裝了一個使用timerfd和epoll實現的定時器
我的思路是
- 定時器類初始化時候直接啓動一個線程,線程只負責定時事件觸發後派發任務
- epoll定時器事件觸發的時候,將註冊的定時事件投遞給任務線程
- 例子中任務線程使用了一個線程池
頭文件
#ifndef CCTimer_H_
#define CCTimer_H_
#include "ThreadPool.h"
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <cstring>
#include <stdint.h>
#include "SafeMap.h"
typedef std::function<void()> TimerCallback;
inline void memZero(void* p, size_t n)
{
std::memset(p, 0, n);
}
struct TimerInfo{
int timerfd;
TimerCallback timerFunc;
uint32_t timerId;
bool isPeriodic;
};
class CCTimer {
public:
CCTimer(const size_t thread_count = 1);
~CCTimer();
bool setTimeEvent(const uint32_t timerId, const uint32_t ms, TimerCallback cb, const bool isPeriodic = false);
bool cancelTimeEvent(const uint32_t timerId);
private:
void handleTimerfdInEpoll();
bool timerIdIsExist(const uint32_t timerId);
bool epollAddTimerFd(const uint32_t timerId, const int timerfd);
bool epollDelTimerFd(const int timerfd);
bool timerFdSetTime(const int timerfd, const uint32_t ms, const bool isPeriodic = false);
bool stopTimerfdSetTime(const int timerfd);
void readTimerfd(int timerfd);
private:
static const int initEventListSize_ = 16;
std::shared_ptr<thread_pool> threadPool_;//靈活掌握
const int epollfd_;
typedef std::vector<struct epoll_event> EventList;
///<觸發的事件填充
EventList events_;
SafeMap<uint32_t, TimerInfo> timerMap_; //這個map如果沒有多線程去操作同一個定時器類對象的話,可以換成普通的map
};
#endif
源文件
#include "Timer.h"
#include <unistd.h>
#include <iostream>
CCTimer::CCTimer(const size_t thread_count):
epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
events_(initEventListSize_)
{
///<創建線程池
threadPool_ = std::make_shared<thread_pool>(thread_count);
///<創建一個線程做epool事件監聽
std::thread t(&CCTimer::handleTimerfdInEpoll, this);
t.detach();
}
CCTimer::~CCTimer()
{
::close(epollfd_);
}
bool CCTimer::setTimeEvent(const uint32_t timerId, const uint32_t ms, TimerCallback cb, const bool isPeriodic)
{
if (timerIdIsExist(timerId)) {
return false;
}
int timerfd = ::timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK | TFD_CLOEXEC);
if (timerfd < 0) {
return false;
}
if (!timerFdSetTime(timerfd, ms, isPeriodic)) {
return false;
}
if (!epollAddTimerFd(timerId, timerfd)) {
return false;
}
timerMap_[timerId].timerFunc = cb;
timerMap_[timerId].isPeriodic = isPeriodic;
return true;
}
bool CCTimer::cancelTimeEvent(const uint32_t timerId)
{
if (!timerIdIsExist(timerId)) {
return false;
}
if (!stopTimerfdSetTime(timerMap_[timerId].timerfd)) {
return false;
}
///< 從epoll循環中去掉fd的監聽
epollDelTimerFd(timerMap_[timerId].timerfd);
///< 從map中刪除timerId
timerMap_.erase(timerId);
return true;
}
bool CCTimer::timerIdIsExist(const uint32_t timerId)
{
return timerMap_.find(timerId) != timerMap_.end();
}
bool CCTimer::timerFdSetTime(const int timerfd, const uint32_t ms, const bool isPeriodic)
{
struct itimerspec newValue;
memZero(&newValue, sizeof newValue);
if (ms >= 1000) {
newValue.it_value.tv_sec = ms / 1000;
}
newValue.it_value.tv_nsec = (ms % 1000) * 1000;
if (isPeriodic) {
newValue.it_interval = newValue.it_value;
}
if (::timerfd_settime(timerfd, 0, &newValue, NULL)) {
return false;
}
return true;
}
bool CCTimer::stopTimerfdSetTime(const int timerfd)
{
struct itimerspec newValue;
memZero(&newValue, sizeof newValue);
if (::timerfd_settime(timerfd, 0, &newValue, NULL)) {
return false;
}
return true;
}
bool CCTimer::epollAddTimerFd(const uint32_t timerId, const int timerfd)
{
struct epoll_event event;
TimerInfo info;
info.timerfd = timerfd;
info.timerId = timerId;
timerMap_[timerId] = info;
memZero(&event, sizeof event);
event.data.ptr = &timerMap_[timerId];
event.events = EPOLLIN;
if (::epoll_ctl(epollfd_, EPOLL_CTL_ADD, timerfd, &event) < 0) {
timerMap_.erase(timerId);
return false;
}
return true;
}
bool CCTimer::epollDelTimerFd(const int timerfd)
{
struct epoll_event event;
memZero(&event, sizeof event);
event.events = EPOLLOUT;
event.data.fd = timerfd;
if (::epoll_ctl(epollfd_, EPOLL_CTL_DEL, timerfd, &event) < 0) {
return false;
}
return true;
}
void CCTimer::readTimerfd(int timerfd)
{
uint64_t howmany;
ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
if (n != sizeof howmany) {
///< error log
return;
}
}
void CCTimer::handleTimerfdInEpoll()
{
while (true) {
int numEvents = ::epoll_wait(epollfd_,
&*events_.begin(),
static_cast<int>(events_.size()),
0);
///< 事件觸發之後就將函數提交給線程池去執行
for (int i = 0; i < numEvents; i++) {
TimerInfo* infoPtr = static_cast<TimerInfo*>(events_[i].data.ptr);
readTimerfd(infoPtr->timerfd);
if (threadPool_ == nullptr) {
infoPtr->timerFunc(); //你要是麼有線程池,就直接執行回調
} else {
threadPool_->execute(infoPtr->timerFunc);//推薦其他線程執行,保證定時準確
}
if (!infoPtr->isPeriodic) {
cancelTimeEvent(infoPtr->timerId);
}
}
///< 說明一次觸發的事件太多,擴大容量
if (static_cast<size_t>(numEvents) == events_.size()) {
events_.resize(events_.size()*2);
}
}
}
使用例子
#include "Timer.h"
#include <iostream>
#include <chrono>
using namespace std;
void printANum(int a) {
std::cout << a << std::endl;
}
void printAString(std::string str)
{
std::cout << str << std::endl;
}
int main (int argc, char** argv)
{
CCTimer cc;
auto f = std::bind(printANum, 100);
auto f2 = std::bind(printAString, "hahahahhaha");
cc.setTimeEvent(1, 1000, f, true);
cc.setTimeEvent(2, 1000, f2, true);
std::chrono::seconds sec(5);
std::this_thread::sleep_for(sec);
cc.cancelTimeEvent(1);
std::chrono::seconds sec2(5);
std::this_thread::sleep_for(sec2);
}