Boost库学习(4)thread 3

这一篇,我们利用thread的知识来实现一个简单的定时器。

功能要求

  • 能设置定时器的间隔时间,回调函数,以及回调函数的执行次数
  • 能通过定时器的ID(编号)手动停止定时器
  • 定时器的ID可以采用手动指定和自动生成两种方式
  • 可以批量删除定时器(借鉴了QT中的思想,所有的UI对象都有一个父对象,父对象删除时,同时删除它的所有子对象)

实现思路

  • 每一个定时器有一个父对象
  • 用一个数组类型对象来管理所有的定时器
  • 用一个线程来循环检测是否有定时器TimeOut(时间到了),对TimeOut的定时器,修改回调函数的执行次数并执行回调函数

程序

<!-- 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目录。

你可能感兴趣的:(C++,boost)