版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hiwubihe/article/details/84206235
分享一个基于C++11实现的定时器,当有多个定时任务时,向定时器里面添加定时任务,定时器到时间自动执行事件,编译环境(GCC) 4.7.2 ,参考代码
Timer.h
#ifndef _X_TIMER_H
#define _X_TIMER_H
#include <map>
#include <unordered_map>
#include <chrono>
#include <functional>
#include <cstdint>
#include <chrono>
#include <memory>
#include <mutex>
#include <thread>
typedef std::function<void(void*)> TimerEvent;
typedef std::pair<int64_t, uint32_t> TimerId;
class Timer
{
public:
Timer(const TimerEvent& event, uint32_t ms, bool repeat,void*pUser)
: eventCallback(event)
, _interval(ms)
, _isRepeat(repeat)
,_pUser(pUser)
{
if (_interval == 0)
_interval = 1;
}
Timer() { }
//定时器是否重复执行
bool isRepeat() const
{
return _isRepeat;
}
static void sleep(unsigned ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
void setEventCallback(const TimerEvent& event)
{
eventCallback = event;
}
//直接执行任务接口
void start(int64_t microseconds, bool repeat=false)
{
_isRepeat = repeat;
auto timeBegin = std::chrono::high_resolution_clock::now();
int64_t elapsed = 0;
do
{
std::this_thread::sleep_for(std::chrono::microseconds(microseconds - elapsed));
timeBegin = std::chrono::high_resolution_clock::now();
eventCallback(_pUser);
elapsed = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - timeBegin).count();
if (elapsed < 0)
elapsed = 0;
} while (_isRepeat);
}
void stop()
{
_isRepeat = false;
}
private:
friend class TimerQueue;
void setNextTimeout(int64_t currentTimePoint)
{
_nextTimeout = currentTimePoint + _interval;
}
int64_t getNextTimeout() const
{
return _nextTimeout;
}
//初始化为C++11—lambda函数
TimerEvent eventCallback = [](void*){};
bool _isRepeat = false;
uint32_t _interval = 0;
int64_t _nextTimeout = 0;
void * _pUser;
};
class TimerQueue
{
public:
TimerId addTimer(const TimerEvent& event, uint32_t ms, bool repeat,void*pUser);
void removeTimer(TimerId timerId);
// 返回最近一次超时的时间, 没有定时器任务返回-1
int64_t getTimeRemaining();
void handleTimerEvent();
private:
int64_t getTimeNow();
std::mutex _mutex;
//添加定时器 MAP会自动排序 依据TimerId->first
std::map<TimerId, std::shared_ptr<Timer>> _timers;
//重复执行定时器列表 unordered_map 采用HASH处理 查找非常快
std::unordered_map<uint32_t, std::shared_ptr<Timer>> _repeatTimers;
uint32_t _lastTimerId = 0;
uint32_t _timeRemaining = 0;
};
#endif
Timer.cpp
#include "Timer.h"
#include <iostream>
using namespace std;
using namespace std::chrono;
TimerId TimerQueue::addTimer(const TimerEvent& event, uint32_t ms, bool repeat,void*pUser)
{
std::lock_guard<std::mutex> locker(_mutex);
int64_t timeoutPoint = getTimeNow();
TimerId timerId = {timeoutPoint+ms, ++_lastTimerId};
//相当new shared_ptr
auto timer = make_shared<Timer>(event, ms, repeat,pUser);
timer->setNextTimeout(timeoutPoint);
if(repeat)
{
//c++11 中添加emplace 和insert 区别时,插入不需要构造临时变量
//参考下面 insert
_repeatTimers.emplace(timerId.second, timer);
}
_timers.insert(std::pair<TimerId,std::shared_ptr<Timer>>(timerId, std::move(timer)));
return timerId;
}
void TimerQueue::removeTimer(TimerId timerId)
{
std::lock_guard<std::mutex> locker(_mutex);
auto iter = _repeatTimers.find(timerId.second);
if(iter != _repeatTimers.end())
{
TimerId t = {iter->second->getNextTimeout(), timerId.second};
_repeatTimers.erase(iter);
_timers.erase(t);
}
else
{
_timers.erase(timerId);
}
}
int64_t TimerQueue::getTimeNow()
{
auto timePoint = steady_clock::now();
return duration_cast<milliseconds>(timePoint.time_since_epoch()).count();
}
int64_t TimerQueue::getTimeRemaining()
{
std::lock_guard<std::mutex> locker(_mutex);
if(_timers.empty())
return -1;
//因为MAP会自动排序 所以每次所有定时任务 都是按照时间先后排列好的
int64_t ms = _timers.begin()->first.first - getTimeNow();
if(ms <= 0)
return 0;
return ms;
}
void TimerQueue::handleTimerEvent()
{
if(!_timers.empty() || !_repeatTimers.empty())
{
std::lock_guard<std::mutex> locker(_mutex);
int64_t timePoint = getTimeNow();
while(!_timers.empty() && _timers.begin()->first.first<=timePoint)
{
_timers.begin()->second->eventCallback(_timers.begin()->second->_pUser);
if(_timers.begin()->second->isRepeat())
{
_timers.begin()->second->setNextTimeout(timePoint);
TimerId t = {_timers.begin()->second->getNextTimeout(), _timers.begin()->first.second};
auto timerPtr = std::move(_timers.begin()->second);
_timers.erase(_timers.begin());
_timers.insert(std::pair<TimerId,std::shared_ptr<Timer>>(t, std::move(timerPtr)));
//_timers.insert(std::pair<TimerId,std::shared_ptr<Timer>>(timerId, std::move(timer)));
}
else
{
_timers.erase(_timers.begin());
}
}
}
}
main.cpp
#include "Timer.h"
#include <stdio.h>
#include <unistd.h>
#define USE3
#ifdef USE1
Timer g_stTimer1;
static int iCnt =0;
void TimerEvent1(void)
{
printf("TimerEvent1 %d execute...\n",iCnt++);
if(iCnt >= 10)
{
//执行10次结束
g_stTimer1.stop();
}
}
#endif
#ifdef USE2
void TimerEvent2(void)
{
printf("TimerEvent2 execute...\n");
}
void TimerEvent3(void)
{
printf("TimerEvent3 execute...\n");
}
#endif
#ifdef USE3
void TimerEventVideo(void*pVideoId)
{
long iVideoId = (long)pVideoId;
printf("TimerEventVideo execute id:%ld...\n",iVideoId);
}
void TimerEventAudio(void*pAudioId)
{
long iAudioId = (long)pAudioId;
printf("TimerEventAudio execute id:%ld...\n",iAudioId);
}
#endif
int main()
{
#ifdef USE1
/////////用法1 BEGIN
g_stTimer1.setEventCallback(TimerEvent1);
//微秒单位 没过2秒执行一次 执行10次结束
int64_t i64interval = 2000000;
g_stTimer1.start(i64interval, true);
printf("用法1结束\n");
return 1;
/////////用法1 END
#endif
#ifdef USE2
//添加两个执行一次的定时器
TimerQueue stTimerQueue;
stTimerQueue.addTimer(TimerEvent2, 6000, false);
stTimerQueue.addTimer(TimerEvent3, 1000, false);
//距离最近下一次执行还剩多少时间
int64_t iTimeRemain = stTimerQueue.getTimeRemaining();
while(iTimeRemain>0)
{
usleep(iTimeRemain*1000);
stTimerQueue.handleTimerEvent();
iTimeRemain = stTimerQueue.getTimeRemaining();
}
//任务执行完成
return 1;
#endif
#ifdef USE3
//实际应用场景 音视频转发时 交替发送音视频 视频帧率25 40ms发送一帧视频
//音频采用率44100 AAC 一帧时间(1024*1000)/44100=23ms 23ms发送一帧
TimerQueue stTimerQueue;
//发送第一帧视频
printf("Send First Video Frame...\n");
long iVedioId = 0;
stTimerQueue.addTimer(TimerEventVideo, 40, true,(void*)iVedioId);
//发送第一帧视频
printf("Send First Audio Frame...\n");
long iAudioId = 1;
stTimerQueue.addTimer(TimerEventAudio, 23, true,(void*)iAudioId);
//距离最近下一次执行还剩多少时间
int64_t iTimeRemain = stTimerQueue.getTimeRemaining();
while(iTimeRemain>0)
{
Timer::sleep(iTimeRemain);
stTimerQueue.handleTimerEvent();
iTimeRemain = stTimerQueue.getTimeRemaining();
}
//任务执行完成
return 1;
#endif
}
CSDN代码下载 点击下载