这一篇,我们利用thread的知识来实现一个简单的定时器。
<!-- lang: cpp -->
template<typename OWNER_T,typename TIMERID_T, TIMERID_T startidx> // OWNER_T:父对象的类型 TIMERID_T:定时器ID的类型 startidx:自动生成ID的起始值
class Timer
{
public:
typedef void (*TimeOutFunc)(TIMERID_T);
Timer(const TIMERID_T &start_timerid=startidx);
~Timer();
/* 生成一个新的定时器并返回定时器ID,第1个参数为回调函数,第2个参数的父对象的指针,为空时表示不设置父对象,第3个参数为手动指定的定时器ID,在(TIMERID_T的最小值,startidx)范围内且没有被占用时有效 */
TIMERID_T timer_new(const TimeOutFunc &func, const boost::shared_ptr<OWNER_T> &ptshare=nullptr, const TIMERID_T &timerid=std::numeric_limits<TIMERID_T>::min());
/* 启动一个定时器,同时设置间隔时间和执行次数 */
bool timer_start(const TIMERID_T &timerid, const boost::chrono::steady_clock::duration &interval=boost::chrono::seconds(1), const long count=1);
/* 停止一个定时器 */
bool timer_stop(const TIMERID_T &timerid);
/* 删除一个定时器 */
bool timer_delete(const TIMERID_T &timerid);
protected:
/* 定时器的数据结构定义 */
class Node
{
public:
Node(const TimeOutFunc &func, const boost::shared_ptr<OWNER_T> &ptshare, const TIMERID_T &timerid)
:m_func(func)
,m_parent(ptshare)
,m_id(timerid)
,m_count(0)
{
}
~Node() {
}
const TIMERID_T& id() const { return m_id; }
bool valid() const { return !m_parent.expired(); }
const boost::chrono::steady_clock::time_point& nexttime() const { return m_nexttime; }
long count() const { return m_count; }
void setInterval(const boost::chrono::steady_clock::duration &interval)
{
m_interval=interval;
}
void setCount(const long count)
{
m_count=count;
}
/* 更新下次TimeOut的时刻 */
void renew(const boost::chrono::steady_clock::time_point &tp=boost::chrono::steady_clock::now()) { if (m_count!=0) {m_nexttime=tp+m_interval;} }
/* 修改回调函数的执行次数并调用回调函数,次数小于0表示无限次 */
void fire()
{
if (m_count==0) {
} else {
if (m_count>0) {
m_count--;
}
m_func(m_id);
}
}
private:
TimeOutFunc m_func;
/* 使用weak_ptr可以检测父对象是否依然生存,同时不影响父对象被回收 */
boost::weak_ptr<OWNER_T> m_parent;
TIMERID_T m_id;
long m_count;
boost::chrono::steady_clock::duration m_interval; // 时间间隔
boost::chrono::steady_clock::time_point m_nexttime; // 下次TimeOut的时刻
};
/* 用于定时器的排序 */
struct SharedNodeCmp
{
bool operator() (const boost::shared_ptr<Node> &n1, const boost::shared_ptr<Node> &n2) const
{
return n1->id()<n2->id();
}
};
/* 定时器线程的无限循环函数 */
void timer_loop();
/* 唤醒定时器线程 */
/* 定时器线程处于睡眠状态的情况:没有有效的定时器,或者正在等待下一次TimeOut */
/* 唤醒线程的情况:程序结束(为了退出无限循环),启动了新的定时器(为了重新计算到下一次TimeOut的等待时间) */
void wakeup_thread(bool adopt, bool terminate);
/* 通过定时器ID查找定时器 */
boost::shared_ptr<Node> get_node_by_timerid(const TIMERID_T &timerid)
{
boost::shared_ptr<Node> node=nullptr;
for(auto &elem:m_nodelist) {
if (elem->id()==timerid) {
node=elem;
break;
} else if (elem->id()>timerid) {
break;
}
}
return node;
}
private:
boost::shared_ptr<OWNER_T> m_sharedOwner; // 默认父对象,用于没有指定父对象的定时器
TIMERID_T m_start; // 自动分配ID的起始值
TIMERID_T m_current; // 下一个自动分配ID的值
boost::thread m_thread; // 定时器线程
boost::condition_variable m_cond; // 唤醒定时器线程用的信号量
boost::mutex m_condmutex; // 信号量用的互斥锁
bool m_termflag; // 定时器结束用flag
std::set<boost::shared_ptr<Node>,SharedNodeCmp> m_nodelist; // 按ID排序的定时器集合
std::list<boost::weak_ptr<Node>> m_timerlist; // 按下次TimeOut时刻排序的定时器列表,使用weak_ptr,不影响m_nodelist删除定时器
};
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
Timer<OWNER_T,TIMERID_T,startidx>::Timer(const TIMERID_T &start_timerid)
:m_sharedOwner(new OWNER_T())
,m_start(start_timerid<=std::numeric_limits<TIMERID_T>::min()?std::numeric_limits<TIMERID_T>::min()+1:start_timerid)
,m_current(m_start)
,m_thread(&Timer<OWNER_T,TIMERID_T,startidx>::timer_loop,this) // 对象创建时启动定时器线程,thread的使用方法与async类似,参数为一个函数
,m_termflag(false)
{
}
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
Timer<OWNER_T,TIMERID_T,startidx>::~Timer()
{
/* 删除对象时,先检查定时器线程 */
if (m_thread.joinable()) {
/* 唤醒定时器线程,并要求退出无限循环 */
wakeup_thread(false,true);
/* 等待定时器线程结束 */
m_thread.join();
}
}
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
TIMERID_T Timer<OWNER_T,TIMERID_T,startidx>::timer_new(const TimeOutFunc &func, const boost::shared_ptr<OWNER_T> &ptshare, const TIMERID_T &timerid)
{
TIMERID_T newid;
boost::lock_guard<boost::mutex> lg(m_condmutex);
if ((timerid<m_start) && (timerid>std::numeric_limits<TIMERID_T>::min()) && (get_node_by_timerid(timerid)==nullptr)) {
/* 使用手动指定的ID */
newid=timerid;
} else {
/* 自动分配ID,并计算下一个ID */
if (get_node_by_timerid(m_current)==nullptr) {
newid=m_current;
do {
if (m_current>=std::numeric_limits<TIMERID_T>::max()) {
m_current=m_start;
} else {
m_current++;
}
if (m_current==newid) {
/* 已经遍历了所有ID,但是没有可用ID */
break;
}
} while(get_node_by_timerid(m_current)!=nullptr);
} else {
/* 没有可用ID */
newid=std::numeric_limits<TIMERID_T>::min();
}
}
if (newid!=std::numeric_limits<TIMERID_T>::min()) {
if (ptshare==nullptr) {
boost::shared_ptr<Node> temp(new Node(func,m_sharedOwner,newid));
m_nodelist.insert(temp);
m_timerlist.push_back(boost::weak_ptr<Node>(temp));
} else {
boost::shared_ptr<Node> temp(new Node(func,ptshare,newid));
m_nodelist.insert(temp);
m_timerlist.push_back(boost::weak_ptr<Node>(temp));
}
} else {
}
return newid;
}
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
bool Timer<OWNER_T,TIMERID_T,startidx>::timer_start(const TIMERID_T &timerid, const boost::chrono::steady_clock::duration &interval, const long count)
{
bool ret=true;
boost::lock_guard<boost::mutex> lg(m_condmutex); // 函数结束时自动释放m_condmutex
/* 查找timerid对应的定时器 */
auto elem=get_node_by_timerid(timerid);
if (elem!=nullptr) {
/* 设置定时器的数据,并唤醒定时器线程 */
elem->setInterval(interval);
elem->setCount(count);
elem->renew();
wakeup_thread(true,false);
ret=true;
} else {
ret=false;
}
return ret;
}
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
bool Timer<OWNER_T,TIMERID_T,startidx>::timer_stop(const TIMERID_T &timerid)
{
bool ret=true;
boost::lock_guard<boost::mutex> lg(m_condmutex);
auto elem=get_node_by_timerid(timerid);
if (elem!=nullptr) {
/* timer_stop函数不需要唤醒定时器线程,因为调用回调函数之前会检查count */
elem->setCount(0);
ret=true;
} else {
ret=false;
}
return ret;
}
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
bool Timer<OWNER_T,TIMERID_T,startidx>::timer_delete(const TIMERID_T &timerid)
{
bool ret=true;
boost::lock_guard<boost::mutex> lg(m_condmutex);
auto pos=m_nodelist.begin();
for(;pos!=m_nodelist.end();++pos) {
if ((*pos)->id==timerid) {
/* 从m_nodelist中删除定时器,这里不需要同时删除m_timerlist,因为从m_nodelist中删除之后,m_timerlist中的相应的定时器指针就失效了 */
m_nodelist.erase(pos);
ret=true;
break;
} else if ((*pos)->id()>timerid) {
ret=false;
break;
}
}
return ret;
}
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
void Timer<OWNER_T,TIMERID_T,startidx>::timer_loop()
{
boost::unique_lock<boost::mutex> ul(m_condmutex);
boost::chrono::steady_clock::time_point now;
boost::chrono::steady_clock::duration waittime;
auto npos=m_nodelist.end();
auto tpos=m_timerlist.end();
while(true) {
if (m_termflag) {
break;
}
/* 从m_nodelist中删除那些父对象已经被回收的定时器 */
for(npos=m_nodelist.begin();npos!=m_nodelist.end();) {
if (!(*npos)->valid()) {
m_nodelist.erase(npos++);
} else {
++npos;
}
}
/* 删除m_timerlist中已经失效的定时器,即已经从m_nodelist中删除的定时器 */
m_timerlist.remove_if(
[](boost::weak_ptr<Timer<OWNER_T,TIMERID_T,startidx>::Node> & item) {
return item.expired();
});
/* 按TimeOut时刻排序,次数为0的定时器排在最后 */
m_timerlist.sort(
[](boost::weak_ptr<Timer<OWNER_T,TIMERID_T,startidx>::Node> & item1, boost::weak_ptr<Timer<OWNER_T,TIMERID_T,startidx>::Node> & item2) {
if (item2.lock()->count()==0) {
return true;
} else if (item1.lock()->count()==0) {
return false;
} else {
return item1.lock()->nexttime()<=item2.lock()->nexttime();
}
});
/* 查找已经TimeOut的定时器并调用相应的回调函数 */
now=boost::chrono::steady_clock::now();
for(tpos=m_timerlist.begin();tpos!=m_timerlist.end();++tpos) {
if ((*tpos).lock()->nexttime()<=now) {
auto elem=(*tpos).lock();
if (elem->count()!=0) {
elem->fire();
elem->renew(now);
} else {
break;
}
} else {
break;
}
}
/* 按TimeOut时刻重新排序,因为上一步的renew()操作会修改TimeOut时刻 */
m_timerlist.sort(
[](boost::weak_ptr<Timer<OWNER_T,TIMERID_T,startidx>::Node> & item1, boost::weak_ptr<Timer<OWNER_T,TIMERID_T,startidx>::Node> & item2) {
if (item2.lock()->count()==0) {
return true;
} else if (item1.lock()->count()==0) {
return false;
} else {
return item1.lock()->nexttime()<=item2.lock()->nexttime();
}
});
/* 计算到下一个TimeOut时刻为止的时间 */
if (m_timerlist.empty()) {
/* 没有定时器时,10秒被唤醒一次 */
waittime=boost::chrono::seconds(10);
} else {
auto elem=m_timerlist.front().lock();
if (elem->count()==0) {
waittime=boost::chrono::seconds(10);
} else {
waittime=elem->nexttime()-now;
}
}
/* 睡眠到下一个TimeOut时刻为止,或者被唤醒 */
m_cond.wait_for(ul,waittime);
}
}
template<typename OWNER_T,typename TIMERID_T,TIMERID_T startidx>
void Timer<OWNER_T,TIMERID_T,startidx>::wakeup_thread(bool adopt, bool terminate)
{
/* 因为没有使用recursive_mutex,所以用第1个参数来表示是否已经处于锁定状态 */
if (adopt) {
m_termflag=terminate;
} else {
boost::lock_guard<boost::mutex> lg(m_condmutex);
m_termflag=terminate;
}
m_cond.notify_one();
}
完整的代码在timer目录。