utils 定时器 (二) 链表

定时器作为常见的工具,要好好掌握。
utils 定时器 (一) 多级时间轮
utils 定时器 (二) 链表
utils 定时器 (三) 最小堆
utils 定时器 (四) 红黑树

整体思路

CTimer代表一个loop的线程
CTimerManager是一个CTimer的容器(map<id, CTimer*>)
同时CTimer是一个std::list<TTimerReq>容器。

CTimer1: [1(11ms), 3(12ms), 2(13ms), 4(14ms)]
CTimer2: [1(11ms), 3(12ms), 2(13ms), 4(14ms)]

轻量级定时器,添加停止删除定时器复杂度都是o(n),链表的性质决定

CTimer中根据超时时间从小到大保存着每个回调函数的ID,回调是通过和CTimer绑定的CTimerListener实现的。CTimerListener可以是一个继承自它的单例类对象,可以是一个全局的session_waiting_context对象等等。

typedef struct
{
    uint32_t reqId;
    TTimeSpec timeout;
} TTimerReq;

头文件

#pragma once

#include <list>
#include <atomic>
#include <string>
#include <pthread.h>
#include <mutex>

using TTimeSpec = struct timespec;

typedef struct
{
    uint32_t reqId;
    TTimeSpec timeout;
} TTimerReq;

class CTimerListener;
class CTimer
{
public:
    CTimer(const std::string &name, uint32_t timerId, CTimerListener *timerListener);
    virtual ~CTimer();

    bool start(uint32_t msec, uint32_t reqId = 0);
    bool stop(uint32_t reqId = 0);

private:
    CTimer(const CTimer &);
    CTimer &operator=(const CTimer &);

    void onTimer(uint32_t reqId);
    void onRun();

    static void *threadFunc(void *arg);

    void addRequest(TTimerReq req);
    void notify();

private:
    volatile uint32_t mTimerId;
    CTimerListener *mTimerListener;
    std::mutex mDataMutex;
    std::list<TTimerReq> mTimerReqList;
    std::string mThreadName;
    std::atomic<bool> mThreadExitFlag{false};
    volatile bool mReqFlag;

    pthread_t mThread;
    pthread_attr_t mThreadAttr;
    pthread_mutex_t mThreadmutex;
    pthread_condattr_t mCondAttr;
    pthread_cond_t mCondition;
};
#pragma once

#include <map>
#include <mutex>
#include <string>

#define TIMER_MGR CTimerManager::getInstance()

class CTimerListener
{
public:
    CTimerListener() {}
    virtual ~CTimerListener() {}

    virtual void onTimer(uint32_t id) = 0;
};

class CTimer;
class CTimerManager
{
public:
    CTimerManager();
    virtual ~CTimerManager();

    static CTimerManager *getInstance();
    static void destroy();

    bool createTimer(const std::string &name, CTimerListener *timerListener);
    bool createTimer(const std::string &name, uint32_t id, CTimerListener *timerListener);

    bool startTimer(uint32_t id, uint32_t msec, uint32_t sub_id=0);
    bool stopTimer(uint32_t id);

private:
    CTimerManager(const CTimerManager &);
    CTimerManager &operator=(const CTimerManager &);

private:
    static CTimerManager *mInstance;
    static std::mutex mDataMutex;
    CTimer *mTimer;
    std::map<uint32_t, CTimer *> mTimerMap;
};

主循环

主循环loop当前队伍头的回调能否执行,如果还不超时就继续条件等待 sleepTime+1ms,下次唤醒时肯定能执行了,也不会造成无效的loop浪费cpu

void CTimer::onRun()
{
    // int pri = getpriority(PRIO_PROCESS, gettid());
    pthread_setname_np(mThread, mThreadName.c_str());

    uint32_t sleepTime = THREAD_INFINITE;
    while (!mThreadExitFlag)
    {
        pthread_mutex_lock(&mThreadmutex);
        if (!mReqFlag)
        {
            if (THREAD_INFINITE != sleepTime && 0 != sleepTime)
            {
                TTimeSpec awakeTime{0x00};
                getTimeSpec(&awakeTime, sleepTime);
                pthread_cond_timedwait(&mCondition, &mThreadmutex, &awakeTime);
            }
            else
            {
                pthread_cond_wait(&mCondition, &mThreadmutex);
            }
        }

        mReqFlag = false;
        pthread_mutex_unlock(&mThreadmutex);

        {
            std::unique_lock<std::mutex> lock(mDataMutex);
            while (!mTimerReqList.empty())
            {
                TTimerReq curReq = mTimerReqList.front();
                TTimeSpec curTime{0x00};
                getTimeSpec(&curTime, 0);
                int tmptime = subTimeSpec(&curReq.timeout, &curTime);
                if (tmptime <= 0)
                {
                    mTimerReqList.pop_front();
                    lock.unlock();
                    onTimer(curReq.reqId);
                    lock.lock();
                }
                else
                {
                    sleepTime = tmptime + 1;
                    break;
                }
            }
            if (mTimerReqList.empty())
            {
                sleepTime = THREAD_INFINITE;
            }
            // lock.unlock();
        }
    }
}

添加定时器

1 根据超时时间的大小,有序的添加到队伍中。
2 如果是添加在队伍头的定时器,需要发送信号,唤醒条件等待中的主循环,重新设置条件等待唤醒时间

void CTimer::addRequest(TTimerReq req)
{
    std::unique_lock<std::mutex> lock(mDataMutex);

    bool needNotify = false;
    auto iter = mTimerReqList.begin();
    for (; iter != mTimerReqList.end(); ++iter)
    {
        if (iter->reqId == req.reqId)
        {
            if (iter == mTimerReqList.begin())
            {
                needNotify = true;
            }
            iter = mTimerReqList.erase(iter);
            break;
        }
    }

    if (!mTimerReqList.empty())
    {
        for (iter = mTimerReqList.begin(); iter != mTimerReqList.end(); ++iter)
        {
            if (subTimeSpec(&req.timeout, &iter->timeout) < 0)
            {
                if (iter == mTimerReqList.begin())
                {
                    needNotify = true;
                }
                break;
            }
        }
    }
    else
    {
        needNotify = true;
    }

    mTimerReqList.insert(iter, req);

    if (needNotify)
    {
        notify();
    }
}

cpp文件

#include "CTimer.hpp"
#include "CTimerManager.hpp"

CTimerManager *CTimerManager::mInstance = nullptr;
std::mutex CTimerManager::mDataMutex;

CTimerManager::CTimerManager() : mTimer(nullptr)
{
}

CTimerManager::~CTimerManager()
{
    if (nullptr != mTimer)
    {
        delete mTimer;
        mTimer = nullptr;
    }
    else
    {
        auto iter = mTimerMap.begin();
        for (; iter != mTimerMap.end(); ++iter)
        {
            delete iter->second;
            iter->second = nullptr;
        }
    }
}

CTimerManager *CTimerManager::getInstance()
{
    std::unique_lock<std::mutex> lock(mDataMutex);
    if (nullptr == mInstance)
    {
        mInstance = new CTimerManager();
    }
    return mInstance;
}

void CTimerManager::destroy()
{
    std::unique_lock<std::mutex> lock(mDataMutex);

    if (nullptr != mInstance)
    {
        delete mInstance;
        mInstance = nullptr;
    }
}

bool CTimerManager::createTimer(const std::string &name, CTimerListener *timerListener)
{
    std::unique_lock<std::mutex> lock(mDataMutex);

    bool ret = false;
    if (mTimerMap.empty())
    {
        if (nullptr == mTimer)
        {
            mTimer = new CTimer(name, 0, timerListener);
        }
        if (nullptr != mTimer)
        {
            ret = true;
        }
    }
    return ret;
}

bool CTimerManager::createTimer(const std::string &name, uint32_t id, CTimerListener *timerListener)
{
    std::unique_lock<std::mutex> lock(mDataMutex);

    bool ret = false;
    if (nullptr == mTimer)
    {
        if (mTimerMap.find(id) == mTimerMap.end())
        {
            CTimer *timer = new CTimer(name, id, timerListener);
            if (nullptr != timer)
            {
                mTimerMap[id] = timer;
                ret = true;
            }
        }
        else
        {
            ret = true;
        }
    }

    return ret;
}

bool CTimerManager::startTimer(uint32_t id, uint32_t msec, uint32_t sub_id)
{
    std::unique_lock<std::mutex> lock(mDataMutex);

    bool ret = false;
    if (nullptr != mTimer)
    {
        mTimer->start(msec, id);
        ret = true;
    }
    else if (mTimerMap.find(id) != mTimerMap.end())
    {
        mTimerMap[id]->start(msec, sub_id);
        ret = true;
    }
    else
    {
    }
    return ret;
}

bool CTimerManager::stopTimer(uint32_t id)
{
    std::unique_lock<std::mutex> lock(mDataMutex);

    bool ret = false;
    if (nullptr != mTimer)
    {
        mTimer->stop(id);
        ret = true;
    }
    else if (mTimerMap.find(id) != mTimerMap.end())
    {
        mTimerMap[id]->stop();
        ret = true;
    }
    else
    {
    }
    return ret;
}
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include "CTimer.hpp"
#include "CTimerManager.hpp"

namespace
{
#define THREAD_STACK_SIZE (64 * 1024)
#define THREAD_INFINITE (0xFFFFFFFF)

    void getTimeSpec(TTimeSpec *time, int msec)
    {
        TTimeSpec now{0x00};
        clock_gettime(CLOCK_MONOTONIC, &now);
        time->tv_sec = now.tv_sec + msec / 1000;
        time->tv_nsec = now.tv_nsec + (msec % 1000) * 1000000;

        if (time->tv_nsec >= 1000000000)
        {
            time->tv_sec++;
            time->tv_nsec -= 1000000000;
        }
    }

    int subTimeSpec(TTimeSpec *time1, TTimeSpec *time2)
    {
        int ms =
            time1->tv_sec * 1000 + time1->tv_nsec / 1000000 - (time2->tv_sec * 1000 + time2->tv_nsec / 1000000);

        return ms;
    }
}

CTimer::CTimer(const std::string &name, uint32_t timerId, CTimerListener *timerListener) : mTimerId(timerId), mTimerListener(timerListener), mTimerReqList(), mThreadName(name), mReqFlag(false)
{
    int ret = 0;
    // init mutex
    ret = pthread_mutex_init(&mThreadmutex, nullptr);
    if (0 != ret)
    {
        // log
    }
    // init condition variable
    pthread_condattr_init(&mCondAttr);
    pthread_condattr_setclock(&mCondAttr, CLOCK_MONOTONIC);
    ret = pthread_cond_init(&mCondition, &mCondAttr);
    if (0 != ret)
    {
        // log
    }
    // create thread
    pthread_attr_init(&mThreadAttr);
    pthread_attr_setstacksize(&mThreadAttr, THREAD_STACK_SIZE);
    ret = pthread_create(&mThread, &mThreadAttr, threadFunc, this);
    if (0 != ret)
    {
        // log
    }
}

CTimer::~CTimer()
{
    if (0 == mThread)
    {
        return;
    }

    {
        std::unique_lock<std::mutex> lock(mDataMutex);
        mTimerReqList.clear();
    }
    mThreadExitFlag = true;
    pthread_cond_signal(&mCondition);
    pthread_join(mThread, nullptr);

    pthread_mutex_destroy(&mThreadmutex);
    pthread_cond_destroy(&mCondition);
    pthread_condattr_destroy(&mCondAttr);
    pthread_attr_destroy(&mThreadAttr);
}

bool CTimer::start(uint32_t msec, uint32_t reqId)
{
    TTimeSpec timeout{0x00};
    getTimeSpec(&timeout, msec);
    TTimerReq req = {reqId, timeout};
    addRequest(req);
    return true;
}

bool CTimer::stop(uint32_t reqId)
{
    std::unique_lock<std::mutex> lock(mDataMutex);
    bool needNotify = false;
    auto iter = mTimerReqList.begin();
    for (; iter != mTimerReqList.end(); ++iter)
    {
        if (iter->reqId == reqId)
        {
            if (iter == mTimerReqList.begin())
            {
                needNotify = true;
            }
            mTimerReqList.erase(iter);
            break;
        }
    }

    if (needNotify)
    {
        notify();
    }

    return true;
}

void CTimer::onTimer(uint32_t reqId)
{
    if (nullptr != mTimerListener)
    {
        mTimerListener->onTimer(reqId);
    }
}

void CTimer::onRun()
{
    // int pri = getpriority(PRIO_PROCESS, gettid());
    pthread_setname_np(mThread, mThreadName.c_str());

    uint32_t sleepTime = THREAD_INFINITE;
    while (!mThreadExitFlag)
    {
        pthread_mutex_lock(&mThreadmutex);
        if (!mReqFlag)
        {
            if (THREAD_INFINITE != sleepTime && 0 != sleepTime)
            {
                TTimeSpec awakeTime{0x00};
                getTimeSpec(&awakeTime, sleepTime);
                pthread_cond_timedwait(&mCondition, &mThreadmutex, &awakeTime);
            }
            else
            {
                pthread_cond_wait(&mCondition, &mThreadmutex);
            }
        }

        mReqFlag = false;
        pthread_mutex_unlock(&mThreadmutex);

        {
            std::unique_lock<std::mutex> lock(mDataMutex);
            while (!mTimerReqList.empty())
            {
                TTimerReq curReq = mTimerReqList.front();
                TTimeSpec curTime{0x00};
                getTimeSpec(&curTime, 0);
                int tmptime = subTimeSpec(&curReq.timeout, &curTime);
                if (tmptime <= 0)
                {
                    mTimerReqList.pop_front();
                    lock.unlock();
                    onTimer(curReq.reqId);
                    lock.lock();
                }
                else
                {
                    sleepTime = tmptime + 1;
                    break;
                }
            }
            if (mTimerReqList.empty())
            {
                sleepTime = THREAD_INFINITE;
            }
            // lock.unlock();
        }
    }
}

void *CTimer::threadFunc(void *arg)
{
    if (nullptr == arg)
    {
        return nullptr;
    }

    static_cast<CTimer *>(arg)->onRun();
    pthread_exit(nullptr);
}

void CTimer::addRequest(TTimerReq req)
{
    std::unique_lock<std::mutex> lock(mDataMutex);

    bool needNotify = false;
    auto iter = mTimerReqList.begin();
    for (; iter != mTimerReqList.end(); ++iter)
    {
        if (iter->reqId == req.reqId)
        {
            if (iter == mTimerReqList.begin())
            {
                needNotify = true;
            }
            iter = mTimerReqList.erase(iter);
            break;
        }
    }

    if (!mTimerReqList.empty())
    {
        for (iter = mTimerReqList.begin(); iter != mTimerReqList.end(); ++iter)
        {
            if (subTimeSpec(&req.timeout, &iter->timeout) < 0)
            {
                if (iter == mTimerReqList.begin())
                {
                    needNotify = true;
                }
                break;
            }
        }
    }
    else
    {
        needNotify = true;
    }

    mTimerReqList.insert(iter, req);

    if (needNotify)
    {
        notify();
    }
}

void CTimer::notify()
{
    pthread_mutex_lock(&mThreadmutex);
    mReqFlag = true;
    pthread_cond_signal(&mCondition);
    pthread_mutex_unlock(&mThreadmutex);
}

用法

CTimerManager中有一个CTimer和一个容器,对应着是否要启动多个组。轻量级只需要启一个相关的定时器,处理和该定时器对应的多个回调ID。多个组就对应启多个线程。

CTimer *mTimer;
std::map<uint32_t, CTimer *> mTimerMap;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值