實現一個簡單的基於asio的定時器
阿新 • • 發佈:2019-01-22
定時器的精度為秒級,程式碼如下:
timer.h
#ifndef ASIO_TIMER_H #define ASIO_TIMER_H #include <map> #include <vector> #include <mutex> #include <boost/asio/io_service.hpp> #include <boost/asio/deadline_timer.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/bind.hpp> #include <boost/function.hpp> #include <boost/asio/placeholders.hpp> typedef void(*ProcessFun)(void*); typedef boost::shared_ptr < boost::asio::deadline_timer> pDeadlineTimer; struct STimerUnit { int id; pDeadlineTimer t; ProcessFun fun; int seconds; }; typedef boost::shared_ptr<STimerUnit> TimerUnitPtr; class CTimer { public: CTimer():m_ioWork(m_ioService),m_lID(0) { } public: //新增一個定時業務,f為業務處理函式,arg為自定義引數,seconds為超時秒數 //返回生成的ID int AddTimerUnit(ProcessFun f, void* arg, int seconds); //每intervalSeconds秒數執行一次 f函式 int AddTimerIntervalUnit(ProcessFun f, void *arg, int intervalSeconds); //刪除指定定時器 void RemoveTimerUnit(int id); bool TimerisValid(int id); void Run(); private: void TimerProcess(int id, void* arg, bool isIntervalTimer,const boost::system::error_code& e); private: std::map<int, TimerUnitPtr> m_mapTimerUnits; private: boost::asio::io_service m_ioService; boost::asio::io_service::work m_ioWork; private: std::mutex m_mutexTimerUnit; private: //分配timer id std::vector<int> m_vecTimerUnitIDs; unsigned long long m_lID; }; #endif
timer.cpp
#include "log.h" #include "asiotimer.h" int CTimer::AddTimerUnit(ProcessFun f, void* arg, int seconds) { TimerUnitPtr s(new STimerUnit); s->seconds = seconds; s->t.reset(new boost::asio::deadline_timer(m_ioService, boost::posix_time::seconds(seconds))); s->fun = f; { std::lock_guard<std::mutex> lock(m_mutexTimerUnit); m_mapTimerUnits.insert(std::make_pair(++m_lID, s)); s->t->async_wait(boost::bind(&CTimer::TimerProcess, this, m_lID, arg, false,boost::asio::placeholders::error)); return m_lID; } } int CTimer::AddTimerIntervalUnit(ProcessFun f, void *arg, int intervalSeconds) { TimerUnitPtr s(new STimerUnit); s->seconds = intervalSeconds; s->t.reset(new boost::asio::deadline_timer(m_ioService, boost::posix_time::seconds(intervalSeconds))); s->fun = f; { std::lock_guard<std::mutex> lock(m_mutexTimerUnit); m_mapTimerUnits.insert(std::make_pair(++m_lID, s)); s->t->async_wait(boost::bind(&CTimer::TimerProcess, this, m_lID, arg, true, boost::asio::placeholders::error)); return m_lID; } } void CTimer::RemoveTimerUnit(int id) { std::lock_guard<std::mutex> lock(m_mutexTimerUnit); std::map<int, TimerUnitPtr>::iterator It = m_mapTimerUnits.find(id); if (It != m_mapTimerUnits.end()) { It->second->t->cancel(); m_mapTimerUnits.erase(It); return; } } bool CTimer::TimerisValid(int id) { std::lock_guard<std::mutex> lock(m_mutexTimerUnit); std::map<int, TimerUnitPtr>::iterator It = m_mapTimerUnits.find(id); if (It != m_mapTimerUnits.end()) { return true; } return false; } void CTimer::Run() { m_ioService.run(); } void CTimer::TimerProcess(int id, void* arg, bool isIntervalTimer, const boost::system::error_code& e) { if (e == boost::asio::error::operation_aborted) { return; } TimerUnitPtr pTimerUnit; { std::lock_guard<std::mutex> lock(m_mutexTimerUnit); std::map<int, TimerUnitPtr>::iterator It = m_mapTimerUnits.find(id); if (It != m_mapTimerUnits.end()) { pTimerUnit = It->second; if (!isIntervalTimer) { m_mapTimerUnits.erase(It); } } LOG_INFO << "=========>mapTimerUnits size " << m_mapTimerUnits.size() << std::endl; } if (pTimerUnit) { pTimerUnit->fun(arg); if (isIntervalTimer) { pTimerUnit->t->expires_at(pTimerUnit->t->expires_at() + boost::posix_time::seconds(pTimerUnit->seconds)); pTimerUnit->t->async_wait(boost::bind(&CTimer::TimerProcess, this, id, arg, true, boost::asio::placeholders::error)); } } else { LOG_INFO << "TimerUnit pointer is NULL" << std::endl; } }
main.cpp
void Print(void *arg) { std::cout << "==============>Print "<<*(int*)arg << std::endl; } void Print1(void *arg) { std::cout << "================>Print1 " << *(int*)arg << std::endl; } void Print2(void *arg) { std::cout << "===================>Print2 " << *(int*)arg << std::endl; } int main() { CTimer t1; std::thread t(std::bind(&CTimer::Run, &t1)); /*int i3 = 3; t1.AddTimerUnit(Print2, &i3,3);*/ //t1.Run(); int i = 1; int id = t1.AddTimerUnit(Print, &i, 5); //t1.RemoveTimerUnit(id); int i2 = 2; t1.AddTimerUnit(Print1, &i2, 2); int i3 = 3; int ID = t1.AddTimerIntervalUnit(Print2, &i3, 1); Sleep(10 * 1000); t1.RemoveTimerUnit(ID); std::system("pause"); }
1/24號,修改一處問題,如果在AddTimerUnit之前呼叫Run方法,io_service會因為沒有任務而直接退出,新增一個io_service::work 成員變數,讓io_service一直保持loop。
添加了AddTimerIntervalUnit介面,用於在每隔intervalSeconds秒數呼叫一次業務處理回撥。
修改了TimerProcess函式,在操作定時器之前先用臨時指標指向map中儲存的定時器物件後,先對map進行刪除操作後,再通過臨時指標呼叫設定的業務回撥函式。這樣做的目的是:如果在TimerProcess函式中直接通過迭代器訪問定時器物件來呼叫業務函式,如:It->second->fun(arg),此時在fun回撥函式中再次呼叫Add函式來新增定位器,此時就會造成迭代器失效,而造成崩潰。