C++11實現的定時器
阿新 • • 發佈:2018-12-01
分享一個基於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程式碼下載 點選下載