c++简单定时器实现

一.红黑树实现

利用红黑树的有序性组织定时事件。由于需要高频的插入和删除,维护红黑树的绝对有序性会带来性能瓶颈,所以该定时器的效率比较一般。(Nginx中的定时器利用红黑树实现)。用小根堆来组织的话,会明显提高插入和删除的的效率,因为它不严格要求有序,维护成本不是太高。比如libevent/libev和golang中的定时器都是用小根堆实现的。


#ifndef TTIMERS_H
#define TTIMERS_H
#include 
#include 
#ifndef _WIN32
#include 
#else
#include 
#endif
#include 
#include 
#include 
#include 

//typedef void(*Callback)(const struct _TimerNodeBase& node);
struct _TimerNodeBase;
using Callback=std::function;
struct _TimerNodeBase
{
    uint64_t expire;
    uint64_t offset_expire;
    int64_t id;
    _TimerNodeBase(int64_t _id, uint64_t _expire) :id(_id), expire(_expire), offset_expire(0){}
    bool operator<(const _TimerNodeBase& node) const
    {
        if (expirenode.expire)
            return false;
        else
            return id locker(time_mx);
        if (timeouts.empty() || expire <= timeouts.crbegin()->expire)
        {
            auto pairs = timeouts.emplace(id, expire, func);
            auto iter = pairs.first;
            if (pairs.second && iter != timeouts.begin())
            {
                iter--;
                const uint64_t* offset_expire = &pairs.first->offset_expire;
                uint64_t* timeoffset = const_cast(offset_expire);
                *timeoffset = iter->offset_expire;
            }
            return static_cast(*pairs.first);
        }
        auto ele = timeouts.emplace_hint(timeouts.crbegin().base(), id, expire, func);
        auto iter = ele;
        iter--;
        const uint64_t* offset_expire = &ele->offset_expire;
        uint64_t* timeoffset = const_cast(offset_expire);
        *timeoffset = iter->offset_expire;
        return static_cast(*ele);
    }
    inline bool DelTimer(const _TimerNodeBase& node)
    {
        struct _TimerNode n(node.id, node.expire, Callback());
        time_mx.lock();
        auto iter = timeouts.find(n);
        if (iter == timeouts.end())
        {
            time_mx.unlock();
            return false;
        }
        timeouts.erase(iter);
        time_mx.unlock();
        return true;
    }
    inline void HandleTimer(uint64_t now)
    {
        if (paused_markspot>0)
        {
            TimerSleep(10);
            return;
        }
        time_mx.lock();
        auto iter = timeouts.begin();
        while (iter != timeouts.end() && (iter->expire + iter->offset_expire <= now))
        {
            printf("[%lld] execute timer id=%lld.\r\n", now, iter->id);
            if (iter->func.operator bool())
            {
                //如果回调函数很耗时,执行回调期间以下操作将被阻塞:
                //AddTimer, DelTimer,StopTimer
                //time_mx.unlock();
                iter->func(*iter);
                //time_mx.lock();
            }
            iter = timeouts.erase(iter);
            continue;
        }
        time_mx.unlock();
    }
    inline int TimeToSleep()
    {
        time_mx.lock();
        auto iter = timeouts.begin();
        if (iter == timeouts.end())
        {
            time_mx.unlock();
            return -1;
        }
        int diss = iter->expire + iter->offset_expire - GetCurrentTimeMs();
        time_mx.unlock();
        return diss>0 ? diss : 0;
    }
    inline void TimerSleep(int msec)
    {
#ifdef _MSC_VER
        Sleep(msec);
#else
        usleep(1000 * msec);
#endif
    }
    inline void TimerPause()
    {
        paused_markspot = GetCurrentTimeMs();
    }
    inline void TimerResume()
    {
        if (paused_markspot <= 0)
            return;
        time_mx.lock();
        auto iter = timeouts.begin();
        while (iter != timeouts.end())
        {
            const uint64_t* offset_expire = &iter->offset_expire;
            uint64_t* timeoffset = const_cast(offset_expire);
            *timeoffset += (GetCurrentTimeMs() - paused_markspot);
            iter++;
        }
        time_mx.unlock();
        paused_markspot = 0;
    }
    inline void TimerStop()
    {
        time_mx.lock();
        auto iter = timeouts.begin();
        while (iter != timeouts.end())
        {
            iter = timeouts.erase(iter);
        }
        time_mx.unlock();
    }
    static inline uint64_t GetCurrentTimeMs()
    {
#ifndef _MSC_VER
        struct timespec ti;
        uint64_t t;
        clock_gettime(CLOCK_MONOTONIC, &ti);
        t = (uint64_t)ti.tv_sec * 1000;
        t += ti.tv_nsec / 1000000;
        return t;//milliseconds
#else
        return GetTickCount64();
#endif
    }
private:
    static inline int64_t GenID()
    {
        return gid++;
    }
    static int64_t gid;
    std::atomic_uint64_t paused_markspot = 0;
    std::mutex time_mx;
    std::set<_TimerNode> timeouts;
};
int64_t Timer::gid = 0;


int main()
{
    Timer tim;
    tim.AddTimer(1000, [](const _TimerNodeBase& node){
        printf("timer expire, node=[id=%lld,expire=%lld, expire_offset=%lld]\r\n", node.id, node.expire, node.offset_expire);
    });
    const _TimerNodeBase& n = tim.AddTimer(2000, [](const _TimerNodeBase& node){
        const _TimerNode& n = static_cast(node);
        printf("timer expire, node=[id=%lld,expire=%lld, expire_offset=%lld]\r\n", n.id, n.expire, n.offset_expire);
    });
    tim.DelTimer(n);
    tim.TimerPause();
    tim.TimerSleep(2000);
    tim.TimerResume();
    while (true)
    {
        int time_sleep = tim.TimeToSleep();
        if (time_sleep>0)
            tim.TimerSleep(time_sleep);
        uint64_t now = Timer::GetCurrentTimeMs();
        tim.HandleTimer(now);
    }
    return 0;
}

#endif
二.时间轮实现

时间轮适用于海量时间密集定时任务。多线程环境下可以做到很小的加锁粒度,效率很高,而红黑树,小根堆以及跳表实现的定时器,对数据操作时都需要将整个结构加锁,效率受限。

linux内核 crontab定时器: 8层时间轮

skynet: 5层时间轮

kafka: 3层时间轮

下面是一个5层时间轮定时器的实现例子,仅供学习参考


//spinlock.h
#ifndef SPINLOCK_H
#define SPINLOCK_H
#include 
struct spinlock {
    std::atomic_bool flag;
};

void spinlock_init(struct spinlock* lock) {
    lock->flag.store(false);
}

void spinlock_lock(struct spinlock* lock) {
    bool expected = false;
    while (!lock->flag.compare_exchange_weak(expected, true)) {}
    //while (__sync_lock_test_and_set(&lock->lock, 1)) {}
}

int spinlock_trylock(struct spinlock* lock) {
    bool expected = false;
    return lock->flag.compare_exchange_weak(expected, true);
    //return __sync_lock_test_and_set(&lock->lock, 1) == 0;
}

void spinlock_unlock(struct spinlock* lock) {
    //__sync_lock_release(&lock->lock);
    lock->flag.store(false);
}

void spinlock_destroy(struct spinlock* lock) {
    (void)lock;
}

#endif

//timerwheel.h
#ifndef __TIMERWHEEL_H__
#define __TIMERWHEEL_H__

#include 
#ifndef _MSC_VER
#include 
#else
#include 
#endif
#define TIME_NEAR_SHIFT 8
#define TIME_NEAR (1 << TIME_NEAR_SHIFT)
#define TIME_NEAR_MASK (TIME_NEAR - 1)

#define TIME_LEVEL_SHIFT 6
#define TIME_LEVEL (1 << TIME_LEVEL_SHIFT)
#define TIME_LEVEL_MASK (TIME_LEVEL - 1)
#define TIME_SCALE 10 //时间精度设为10毫秒

typedef void (*handler_ptr)(void* usrdata, int datalen);

typedef struct timer_node {
    uint32_t expire;
    handler_ptr callback;
    void* data;
    int datalen;
    uint8_t cancel;
    int tid;
} timer_node_t;

timer_node_t* add_timer(int tid,int time, handler_ptr func, void* data, int len,int count=0);

void del_timer(timer_node_t* node);

void expire_timer(void);

void init_timer(void);

void clear_timer(void);

#endif

//timerwheel.cc
#include "spinlock.h"
#include "timerwheel.h"

typedef struct timer_node_internal
{
    struct timer_node node;
    int count;
    int duration;
    struct timer_node_internal* next;
}timer_node_it;

typedef struct link_list {
    timer_node_it head;
    timer_node_it* tail;
} link_list_t;

typedef struct timer {
    link_list_t nearest[TIME_NEAR];
    link_list_t t[4][TIME_LEVEL];
    struct spinlock lock;
    uint32_t time_tick;//第一层的时间刻度, 根据时间精度, 每一次加加1(即如果精度为10ms, 则每10ms加1)
    uint64_t timestamp_ref;//参考时间戳
} s_timer_t;

static s_timer_t* TI = NULL;

/**
 * @brief 移除list下面所有的节点
 * @param {link_list_t} *list
 * @return {timer_node_it} *ret 节点的链表头
 */
timer_node_it* link_clear(link_list_t* list)
{
    timer_node_it* ret = list->head.next;
    list->head.next = 0;
    list->tail = &(list->head);
    return ret;
}

void link(link_list_t* list, timer_node_it* node)
{
    list->tail->next = node;
    list->tail = node;
    node->next = 0;
}

void add_node(s_timer_t* T, timer_node_it* node)
{
    uint32_t time = node->node.expire;
    uint32_t current_time = T->time_tick;
    uint32_t msec = time - current_time;

    if (msec < TIME_NEAR) { //[0, 0x100)
        link(&T->nearest[time & TIME_NEAR_MASK], node);
    }
    else if (msec < (1 << (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT))) { //[0x100, 0x4000)
        link(&T->t[0][((time >> TIME_NEAR_SHIFT) & TIME_LEVEL_MASK)], node);
    }
    else if (msec < (1 << (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT))) { //[0x4000, 0x100000)
        link(&T->t[1][((time >> (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node);
    }
    else if (msec < (1 << (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT))) { //[0x100000, 0x4000000)
        link(&T->t[2][((time >> (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node);
    }
    else { //[0x4000000, 0xffffffff]
        link(&T->t[3][((time >> (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node);
    }
}

/**
 * @brief 从timer子数组(level)取出idx的链表节点, 并重新添加到timer中
 * @param {s_timer_t} *T
 * @param {int} level
 * @param {int} idx
 * @return {*}
 */
void move_list(s_timer_t* T, int level, int idx)
{
    timer_node_it* current = link_clear(&T->t[level][idx]);

    while (current) {
        timer_node_it* temp = current->next;
        add_node(T, current);
        current = temp;
    }
}

/**
 * @brief 重新映射, 将该层时间节点下的链表重新映射到上一层去
 * @param {s_timer_t} *T
 * @return {*}
 */
void timer_shift(s_timer_t* T)
{
    int mask = TIME_NEAR;
    uint32_t ct = ++T->time_tick;

    if (ct == 0) {
        move_list(T, 3, 0);
    }
    else {
        // ct / 256
        uint32_t time = ct >> TIME_NEAR_SHIFT;
        int i = 0;

        // ct % 256 == 0
        while ((ct & (mask - 1)) == 0) {
            int idx = time & TIME_LEVEL_MASK;

            if (idx != 0) {
                move_list(T, i, idx);
                break;
            }
            mask <<= TIME_LEVEL_SHIFT;
            time >>= TIME_LEVEL_SHIFT;
            ++i;
        }
    }
}

/**
 * @brief 执行所有节点的回调
 * @param {timer_node_it} *current
 * @return {*}
 */
void dispatch_list(s_timer_t* T,timer_node_it* current)
{
    do {
        timer_node_it* temp = current;
        current = current->next;
        if (temp->node.cancel == 0) {
            temp->node.callback(temp->node.data,temp->node.datalen);
        }
        if (temp->node.cancel != 0) 
        {
            free(temp);
            continue;
        }
        if (temp->count < 0||--temp->count>0)
        {
            temp->next = NULL;
            temp->node.expire = temp->duration + T->time_tick;
            spinlock_lock(&T->lock);
            add_node(T, temp);
            spinlock_unlock(&T->lock);
        }
        else
        {
            free(temp);
        }
    } while (current);
}

void timer_execute(s_timer_t* T)
{
    int idx = T->time_tick & TIME_NEAR_MASK;

    while (T->nearest[idx].head.next) {
        timer_node_it* current = link_clear(&T->nearest[idx]);
        spinlock_unlock(&T->lock);
        dispatch_list(T,current);
        spinlock_lock(&T->lock);
    }
}

/**
 * @brief 更新定时器
 * @param {s_timer_t} *T
 * @return {*}
 */
void timer_update(s_timer_t* T)
{
    spinlock_lock(&T->lock);
    timer_execute(T);
    timer_shift(T);
    timer_execute(T);
    spinlock_unlock(&T->lock);
}

s_timer_t* timer_create_timer(void)
{
    s_timer_t* timer = (s_timer_t*)malloc(sizeof(s_timer_t));
    memset(timer, 0, sizeof(s_timer_t));

    int i, j;
    for (i = 0; i < TIME_NEAR; i++) {
        link_clear(&timer->nearest[i]);
    }
    for (i = 0; i < sizeof(timer->t) / sizeof(timer->t[0]); i++) {
        for (j = 0; j < TIME_LEVEL; j++) {
            link_clear(&timer->t[i][j]);
        }
    }

    spinlock_init(&timer->lock);
    return timer;
}

uint64_t get_current_time(void)
{
#ifndef _MSC_VER
    struct timespec ti;
    uint64_t t;
    clock_gettime(CLOCK_MONOTONIC, &ti);
    t = (uint64_t)ti.tv_sec * 1000;
    t += ti.tv_nsec / 1000000;//milliseconds
    return t/TIME_SCALE;
#else
    return GetTickCount64()/ TIME_SCALE;
#endif
}


/**
 * @brief 添加定时任务
 * @param {int} threadid
 * @param {int} time delayed
 * @param {handler_pt} func
 * @param {void*} user data
 * @param {int} data len
 * @param {int} task count, default 0 means oneshot executing, negiative value means endless executing.
 * @return {timer_node_t*}
 */
timer_node_t* add_timer(int tid,int time, handler_ptr func, void* data, int len,int count)
{
    timer_node_it* node = (timer_node_it*)malloc(sizeof(timer_node_it));
    spinlock_lock(&TI->lock);
    node->node.expire = time + TI->time_tick;
    node->node.callback = func;
    node->node.data = data;
    node->node.datalen = len;
    node->node.tid = tid;
    node->node.cancel = 0;
    node->count = count;
    node->duration = time;
    node->next = NULL;
    if (time <= 0) {
        node->node.callback(node->node.data,node->node.datalen);
        free(node);
        spinlock_unlock(&TI->lock);
        return NULL;
    }

    add_node(TI, node);
    spinlock_unlock(&TI->lock);

    return &node->node;
}

/**
 * @brief 取消定时任务
 * @param {timer_node_t} *node
 * @return {*}
 */
void del_timer(timer_node_t* node)
{
    node->cancel = 1;
}

/**
 * @brief 遍历定时器, 并执行过期的定时器回调
 * @param {*}
 * @return {*}
 */
void expire_timer(void)
{
    uint64_t cp = get_current_time();

    if (cp != TI->timestamp_ref) {
        uint32_t diff = (uint32_t)(cp - TI->timestamp_ref);
        TI->timestamp_ref = cp;
        int i;
        for (i = 0; i < diff; i++) {
            timer_update(TI);
        }
    }
}

void init_timer(void)
{
    TI = timer_create_timer();
    TI->timestamp_ref = get_current_time();
}

void clear_timer(void)
{
    int i, j;

    for (i = 0; i < TIME_NEAR; i++) {
        link_list_t* list = &TI->nearest[i];
        timer_node_it* current = list->head.next;

        while (current) {
            timer_node_it* temp = current;
            current = current->next;
            free(temp);
        }
        link_clear(&TI->nearest[i]);
    }

    for (i = 0; i < sizeof(TI->t) / sizeof(TI->t[0]); i++) {
        for (j = 0; j < TIME_LEVEL; j++) {
            link_list_t* list = &TI->t[i][j];
            timer_node_it* current = list->head.next;

            while (current) {
                timer_node_it* temp = current;
                current = current->next;
                free(temp);
            }
            link_clear(&TI->t[i][j]);
        }
    }
}

//main.cc
#include "timerwheel.h"
#include 
#include 
#include 

static int temp_flag = 0;
void timer_func_cb(void* data, int len)
{
    temp_flag--;
    printf("+++ data address=%#x,data length=%d\n",data,len);
}
int main()
{
    printf("+++ func: %s, line: %d +++\n", __FUNCTION__, __LINE__);
    init_timer();
    timer_node_t* delay_task = add_timer(100,600, timer_func_cb,NULL,0,-1);
    temp_flag = 3;

    while (temp_flag) {
        expire_timer();
        std::this_thread::sleep_for(std::chrono::microseconds(200));//休眠时间要小于时间精度(10ms),因为每个定时事件的处理也需要消耗一定的时间
    }
    printf("+++ func: %s, line: %d +++\n", __FUNCTION__, __LINE__);
    clear_timer();
    return 0;
}

你可能感兴趣的:(后端,r-tree,c++)