【Linux】C/C++ 一种简单易用的高效定时器实现

前言

       第一次在项目上开发 Linux 应用程序,凭借着自己以前自学时开发的小应用程序(Windows)积累的经验,驾驭起来还不算吃力,只是C++需要边学边用。万丈高楼平地起,很多功能看似简单,却需要很多基础的功能来支撑,就如本文的定时器一样。

      在 Windows 上开发有一组定时器API接口 如 SetTimer() 等,Linux 内核也有一组定时器API接口init_timer()\add_timer()等,想着 Linux 应用层也应该有类似的接口,搜索了半天,大部分都只是介绍如何计时,如 SIGALRM、RTC、select...,也有文章介绍过采用多级链表来实现,但令我诧异的是,似乎没有现成的API接口可以用。

简介

     习惯造轮子的我,设想了一个基于条件变量的超时机制实现的较高精度又简单的定时器,经过实际测试验证可行。之所以利用条件变量的超时机制,是为了满足可以随时打断的需求,同时又不需要轮询的去查,在等待的过程中,线程处于阻塞休眠状态,所以也不消耗 CPU 资源。(信号量也可以)

    该机制主要可以应用在一些要求可以随时取消和修改计时时间的场景,例如收到某条启动指令之后需要启动设备,当超过多长时间没有收到指令时就需要停止设备。优点吧,实现简单,精度较高,好用,不消耗 CPU 资源,支持单次和重复触发,并且各个定时器独立计时和执行,不影响其他定时器。缺点吧,就是有点耗内存了,启动 100 个定时器,就有 100 个线程(指的是100个定时器都没有超时)。

    基本上可以满足大部分场景,如果比较庞大的系统需要上百个定时器计时,这种实现方式就不合适了,建议采用多级链表来实现,并且需要考虑定时器与定时器之间影响。

实现

核心源码(我的源码格式很整齐,不知道为什么复制到这里就有点乱了)

代码仅限于提供一种可行的实现思路,未考虑其他(如深度拷贝)等情况。

/**
******************************************************************************
* @文件		Timer.h
* @版本		V1.0.0
* @日期
* @概要		定时器类的实现
* @作者		lmx
* @邮箱		[email protected]
* @博客		https://me.csdn.net/lovemengx
******************************************************************************
**/

#ifndef __TIMER_H
#define __TIMER_H

#include <thread>
#include <mutex> 
#include <condition_variable>

using namespace std;

class Timer 
{
	
public:

	// 定时器工作模式
	typedef enum 
	{
		TIMER_MODE_TYPE_SINGLE, 					// 单次触发模式
		TIMER_MODE_TYPE_REPEAT						// 重复触发模式
	} TIMER_MODE_TYPE_E;			
			
	// 定时器事件类型
	typedef enum {		
		TIMER_EVENT_TYPE_CANCEL, 					// 取消事件
		TIMER_EVENT_TYPE_COMPLETE					// 定时到达
	} TIMER_EVENT_TYPE_E;		
			
	// 回调函数退出类型
	typedef enum {		
		TIMER_EXIT_TYPE_NORMAL, 					// 正常退出
		TIMER_EXIT_TYPE_TERMINATION					// 终止计时(适用于重复触发模式)
	} TIMER_EXIT_TYPE_E;
	
	// 回调函数原型
	typedef TIMER_EXIT_TYPE_E TimerProcCb(TIMER_EVENT_TYPE_E type, void *pArgs);
	
	// 定时器参数
	typedef struct
	{
		TIMER_MODE_TYPE_E 	        mode;			// 工作模式(单次触发, 重复触发)
		unsigned long 		        milliSecond;		// 定时时间(毫秒)
		TimerProcCb 		        *pTimerProcCb;		// 回调函数
		void				*pArgs;			// 传给回调函数的参数
	}TTimerPara;	
	
public:		
	
	Timer();	
	int initPara(TTimerPara *pTimerPara);			// 初始化参数, 可以重复初始化, 下次循环或 start 生效
	int start();						// 启动定时器
	int start(unsigned long milliSecond);			// 设定计时时间并启动定时器
	int modify(unsigned long milliSecond);			// 修改定时器时间
	int reset();						// 重启定时器
	int stop();						// 停止定制器
	
private:
	
	TTimerPara				m_TimerPara;			// 保存所设置的参数
	
private:

	bool					m_ThreadExitFlag;		// 线程退出标志
	mutex					m_TimerMutex;			// 线程互斥锁
	condition_variable		        m_TimerCond;                    // 条件变量, 用于实现即时和打断
	thread					m_TimerThread;			// 计时线程句柄
	static void threadTimerProcess(Timer *pTimer);	// 计时线程函数
};
/**
******************************************************************************
* @文件		Timer.cpp
* @版本		V1.0.0
* @日期
* @概要		定时器类的实现
* @作者		lmx
* @邮箱		[email protected]
* @博客		https://me.csdn.net/lovemengx
******************************************************************************
**/

#include <iostream>
#include <unistd.h>

#include "../common.h"
#include "Timer.h"

// 定时器线程处理函数
void Timer::threadTimerProcess(Timer *pTimer)
{
	TIMER_EXIT_TYPE_E 	        exType		= TIMER_EXIT_TYPE_TERMINATION;
	bool 				exFlag 		= false;
	bool				isTimeout 	= false;
	TTimerPara			timerPara;
	
	while(!exFlag)
	{
		// 拷贝配置, 超时等待
		unique_lock< mutex > lock(pTimer->m_TimerMutex);
		memcpy(&timerPara, &pTimer->m_TimerPara, sizeof(TTimerPara));
		exFlag = pTimer->m_ThreadExitFlag;
		if(!exFlag){
			isTimeout = (cv_status::timeout == pTimer->m_TimerCond.wait_for(lock, chrono::milliseconds(timerPara.milliSecond)));
		}
		else{
			isTimeout = false;
		}
		lock.unlock();

		//  发出事件 TIMER_EVENT_TYPE_CANCEL 
		if(false == isTimeout){
			timerPara.pTimerProcCb(TIMER_EVENT_TYPE_CANCEL, timerPara.pArgs);
			break;
		}

		//  发出事件 TIMER_EVENT_TYPE_COMPLETE
		exType = timerPara.pTimerProcCb(TIMER_EVENT_TYPE_COMPLETE, timerPara.pArgs);
		if(TIMER_MODE_TYPE_SINGLE == timerPara.mode || TIMER_EXIT_TYPE_TERMINATION == exType){
			break;
		}
	}
	
	return ;
}

Timer::Timer()
{
	memset(&m_TimerPara, 0, sizeof(TTimerPara));
}

// 初始化参数
int Timer::initPara(TTimerPara *pTimerPara)
{
	CHECK_POINTER_PARAMETER(pTimerPara);
	CHECK_POINTER_PARAMETER(pTimerPara->pTimerProcCb);
	
	unique_lock< mutex > lock(m_TimerMutex);
	memcpy(&m_TimerPara, pTimerPara, sizeof(TTimerPara));
	lock.unlock();
	
	return 0;
}

// 启动定时器
int Timer::start()
{
	CHECK_POINTER_PARAMETER(m_TimerPara.pTimerProcCb);
	
	// 设置标志位
	unique_lock< mutex > lock(m_TimerMutex);
	m_ThreadExitFlag = false;
	lock.unlock();
	
	// 启动线程
	m_TimerThread = thread(threadTimerProcess, this);
	m_TimerThread.detach();

	return 0;
}

// 设定指定时间并启动定时器
int Timer::start(unsigned long milliSecond)
{

	modify(milliSecond);
	start();
	
	return 0;
}

// 修改定时器时间
int Timer::modify(unsigned long milliSecond)
{

	unique_lock< mutex > lock(m_TimerMutex);
	m_TimerPara.milliSecond = milliSecond;
	lock.unlock();
	
	return 0;
}

// 重置定时器
int Timer::reset()
{

	stop();
	start();

	return 0;
}

// 停止定时器
int Timer::stop()
{

	unique_lock< mutex > lock(m_TimerMutex);
	m_ThreadExitFlag = true;
	lock.unlock();
	m_TimerCond.notify_all();

	return 0;
}

测试

用 Timer 类场景一个对象就是一个定时器,需要多个定时器就创建多个对象即可。


#ifndef __TEST_TIMER_H
#define __TEST_TIMER_H


class TestTimer
{
public:
	int test();
};
		
#endif

#include <iostream>
#include <thread>
#include <unistd.h>
#include "../common.h"
#include "Timer.h"
#include "test_Timer.h"

Timer::TIMER_EXIT_TYPE_E TimerProcRepeatCb(Timer::TIMER_EVENT_TYPE_E type, void *pArgs)
{
	iprintf("id:%d type:%s", *(int *)pArgs, Timer::Timer::TIMER_EVENT_TYPE_COMPLETE == type ? "COMPLETE" : "CANCEL");
	return Timer::TIMER_EXIT_TYPE_NORMAL;
}

int TestTimer::test()
{
	#define TEST_NUMBER	10
	Timer mTimerGrep[TEST_NUMBER];
	int   id[TEST_NUMBER];
	
	Timer::TTimerPara mTimerPara;
	
	iprintf("start...");
	for(int i = 0; i < TEST_NUMBER; i++)
	{
		memset(&mTimerPara, 0, sizeof(Timer::TTimerPara));
		mTimerPara.mode 	= Timer::TIMER_MODE_TYPE_REPEAT;
		mTimerPara.milliSecond 	= 1000;
		mTimerPara.pTimerProcCb = TimerProcRepeatCb;
		mTimerPara.pArgs	= &id[i];
	
		id[i] = i;
		mTimerGrep[i].initPara(&mTimerPara);
		mTimerGrep[i].start();
	}
	
	sleep(10);
	
	iprintf("stop...");
	for(int i = 0; i < TEST_NUMBER; i++)
	{
		mTimerGrep[i].stop();
	}
	
	while(1) sleep(1);
	
	return 0;
}

执行结果

[2020-01-05 01:08:55.901][inf][test_Timer.cpp] test():00037 start... <--
[2020-01-05 01:08:56.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:08:56.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:08:56.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:08:56.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:08:56.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:08:56.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:08:56.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:08:56.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:08:56.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:08:56.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:08:57.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:08:57.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:08:57.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:08:57.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:08:57.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:08:57.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:08:57.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:08:57.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:08:57.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:08:57.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:08:58.908][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:08:58.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:08:58.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:08:58.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:08:58.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:08:58.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:08:58.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:08:58.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:08:58.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:08:58.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:08:59.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:08:59.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:08:59.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:08:59.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:08:59.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:08:59.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:08:59.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:08:59.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:08:59.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:08:59.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:09:00.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:09:00.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:09:00.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:09:00.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:09:00.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:09:00.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:09:00.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:09:00.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:09:00.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:09:00.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:09:01.909][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:09:01.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:09:01.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:09:01.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:09:01.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:09:01.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:09:01.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:09:01.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:09:01.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:09:01.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:09:02.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:09:02.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:09:02.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:09:02.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:09:02.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:09:02.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:09:02.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:09:02.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:09:02.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:09:02.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:09:03.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:09:03.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:09:03.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:09:03.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:09:03.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:09:03.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:09:03.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:09:03.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:09:03.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:09:03.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:09:04.910][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:COMPLETE <--
[2020-01-05 01:09:04.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:COMPLETE <--
[2020-01-05 01:09:04.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:COMPLETE <--
[2020-01-05 01:09:04.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:COMPLETE <--
[2020-01-05 01:09:04.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:COMPLETE <--
[2020-01-05 01:09:04.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:COMPLETE <--
[2020-01-05 01:09:04.911][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:COMPLETE <--
[2020-01-05 01:09:04.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:COMPLETE <--
[2020-01-05 01:09:04.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:COMPLETE <--
[2020-01-05 01:09:04.912][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:COMPLETE <--
[2020-01-05 01:09:05.905][inf][test_Timer.cpp] test():00053 stop... <--
[2020-01-05 01:09:05.906][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:0 type:CANCEL <--
[2020-01-05 01:09:05.906][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:1 type:CANCEL <--
[2020-01-05 01:09:05.906][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:2 type:CANCEL <--
[2020-01-05 01:09:05.906][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:3 type:CANCEL <--
[2020-01-05 01:09:05.907][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:4 type:CANCEL <--
[2020-01-05 01:09:05.907][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:5 type:CANCEL <--
[2020-01-05 01:09:05.907][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:6 type:CANCEL <--
[2020-01-05 01:09:05.907][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:7 type:CANCEL <--
[2020-01-05 01:09:05.907][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:8 type:CANCEL <--
[2020-01-05 01:09:05.907][inf][test_Timer.cpp] TimerProcRepeatCb():00025 id:9 type:CANCEL <--

备注:

1. 代码里面的 CHECK_POINTER_PARAMETER() 是一个检查指针是否为空的宏定义,可以稍微修改一下,建议每个提供给外部的函数入口都判断一下参数的合法性,前期可以避免很多问题。

2. 代码里面的 iprintf() 也是一个宏,就只是附带输出了时间、源码文件名、函数名、行号等信息,与 Timer 的实现无关。

发布了3 篇原创文章 · 获赞 0 · 访问量 293

猜你喜欢

转载自blog.csdn.net/lovemengx/article/details/103839279