Linux下C++实现一个定时器
要在Linux下实现一个定时器,可以使用使用系统提供的定时器API
Linux系统提供了一些定时器API,如setitimer、timer_create、timer_gettime等,可以使用这些API来实现定时器。以setitimer为例,可以按照以下步骤来使用:
1.定义一个itimerval结构体变量,该结构体包含定时器的初始值和定时器到期后的动作。
2.使用setitimer函数启动定时器,该函数接受一个定时器类型参数和一个itimerval结构体参数。
3.通过SIGALRM信号来处理定时器到期后的动作,需要安装一个SIGALRM信号的处理函数。
代码如下:
#pragma once
#include <sys/timerfd.h>
#include <atomic>
#include <chrono>
#include <ctime>
#include <functional>
#include <thread>
namespace pickup {
class Timer {
public:
using TimerfdCallBack = std::function<void()>;
/**
* Create and start a timer
* @param timeout Timeout in milliseconds before @p cb is called, zero disarms timer
* @param period For periodic timers this is the period time that @p timeout is reset to
* @param cb Callback function
*
* Note, the @p timeout value must be non-zero. Setting it to zero
* disarms the timer. This is the behavior of the underlying Linux
* function [timerfd_settimer(2)](https://man7.org/linux/man-pages/man2/timerfd_settime.2.html)
*
*/
Timer(int timeout, int period, TimerfdCallBack&& cb);
~Timer();
void start();
void stop();
bool isRunning() const { return is_running_; }
private:
int createTimerfd();
bool setTimer(int timeout, int period);
void threadFunc();
void handleRead();
private:
// 定时器的起始时间
int timeout_;
// 定时器的间隔时间
int period_;
// 定时器文件描述符
int timerfd_;
// 回调函数
TimerfdCallBack callback_;
// 定时器工作线程
std::thread thread_;
// 是否运行中
std::atomic<bool> is_running_;
};
}; // namespace pickup
#include "pickup/timer/Timer.h"
#include <errno.h>
#include <string.h>
#include <sys/poll.h>
#include <unistd.h>
namespace pickup {
static void msec2tspec(int msec, struct timespec* ts) {
if (msec > 0) {
ts->tv_sec = msec / 1000;
ts->tv_nsec = (msec % 1000) * 1000000;
} else {
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
}
Timer::Timer(int timeout, int period, TimerfdCallBack&& cb)
: timeout_(timeout), period_(period), timerfd_(createTimerfd()), callback_(cb), is_running_(false) {}
Timer::~Timer() {
close(timerfd_);
timerfd_ = -1;
}
void Timer::start() {
if (is_running_) return;
if (!setTimer(timeout_, period_)) return;
is_running_ = true;
thread_ = std::thread(&Timer::threadFunc, this);
}
void Timer::stop() {
if (!is_running_) return;
is_running_ = false;
if (thread_.joinable()) {
thread_.join();
}
}
void Timer::threadFunc() {
int timeout = 5000; // 超时时间(单位:毫秒)
struct pollfd pfd;
bzero(&pfd, sizeof(pfd));
pfd.fd = timerfd_;
pfd.events = POLLIN;
while (is_running_) {
int nready = poll(&pfd, 1, timeout);
if (nready < 0) {
if (errno == EINTR) {
continue;
} else {
perror("poll() failed");
break;
}
} else if (0 == nready) {
printf("poll() timed out.\n");
continue;
} else {
if (pfd.events & POLLIN) {
// 先读取fd中的数据
handleRead();
// 再调用回调函数
callback_();
}
}
}
}
int Timer::createTimerfd() {
int timerfd = timerfd_create(CLOCK_REALTIME, 0);
if (-1 == timerfd) {
perror("timerfd_create");
}
return timerfd;
}
bool Timer::setTimer(int timeout, int period) {
struct itimerspec time;
msec2tspec(timeout, &time.it_value);
msec2tspec(period, &time.it_interval);
int ret = timerfd_settime(timerfd_, 0, &time, NULL);
if (-1 == ret) {
perror("timerfd_settime");
return false;
}
return true;
}
void Timer::handleRead() {
uint64_t expirations = 0;
ssize_t result = read(timerfd_, &expirations, sizeof(expirations));
if (result == -1) {
perror("timerfd read");
}
}
} // namespace pickup