utils 定时器 (三) 最小堆

utils 定时器 (三) 最小堆

utils 定时器 (一) 多级时间轮
utils 定时器 (二) 链表
utils 定时器 (三) 最小堆
utils 定时器 (四) 红黑树

性质

最小堆定时器和红黑树链表定时器思想一致,有序的容器装定时器,定时触发。优点在于最小堆的完全二叉树性质,使得其复杂度相比于红黑树更精准的log(n)。

  1. 是⼀颗完全⼆叉树;
  2. 某⼀个节点的值总是⼩于等于它的⼦节点的值;
  3. 堆中每个节点的⼦树都是最⼩堆;

minheap.h

  • 插入新节点(logn),即尾插,做上浮的操作。
  • 删除旧节点(logn),和last的定时器互换,size–,last定时器在新位置做下沉操作,如果没有下沉,尝试上浮。也可以lazy delete,搞个标志位 or cb_func == NULL
  • 注意定时器结构需要记录自己在堆中的位置(回指),同时需要一个全局变量可以快速的访问该定时器(每个定时器有唯一ID),在这里使用map结构
#pragma once

#include <vector>
#include <map>
using namespace std;

typedef void (*TimerHandler) (struct TimerNode *node);

struct TimerNode {
    int idx = 0;
    int id = 0;
    unsigned int expire = 0;
    TimerHandler cb = NULL;
};

class MinHeapTimer {
public:
    MinHeapTimer() {
        _heap.clear();
        _map.clear();
    }
    static inline int Count() {
        return ++_count;
    }

    int AddTimer(uint32_t expire, TimerHandler cb);
    bool DelTimer(int id);
    void ExpireTimer();

private:
    inline bool _lessThan(int lhs, int rhs) {
        return _heap[lhs]->expire < _heap[rhs]->expire;
    }
    bool _shiftDown(int pos);
    void _shiftUp(int pos);
    void _delNode(TimerNode *node);

private:
    vector<TimerNode*>  _heap;
    map<int, TimerNode*> _map;
    static int _count;
};

int MinHeapTimer::_count = 0;

minheap.cpp

#include <unistd.h>
#if defined(__APPLE__)
#include <AvailabilityMacros.h>
#include <sys/time.h>
#include <mach/task.h>
#include <mach/mach.h>
#else
#include <time.h>
#endif

#include <iostream>

#include "minheap.h"

static uint32_t
current_time() {
	uint32_t t;
#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
	struct timespec ti;
	clock_gettime(CLOCK_MONOTONIC, &ti);
	t = (uint32_t)ti.tv_sec * 1000;
	t += ti.tv_nsec / 1000000;
#else
	struct timeval tv;
	gettimeofday(&tv, NULL);
	t = (uint32_t)tv.tv_sec * 1000;
	t += tv.tv_usec / 1000;
#endif
	return t;
}

int MinHeapTimer::AddTimer(uint32_t expire, TimerHandler cb) {
    int64_t timeout = current_time() + expire;
    TimerNode* node = new TimerNode;
    int id = Count();
    node->id = id;
    node->expire = timeout;
    node->cb = cb;
    node->idx = (int)_heap.size();
    _heap.push_back(node);
    _shiftUp((int)_heap.size() - 1);
    _map.insert(make_pair(id, node));
    return id;
}

bool MinHeapTimer::DelTimer(int id)
{
    auto iter = _map.find(id);
    if (iter == _map.end())
        return false;
    _delNode(iter->second);
    return true;
}

void MinHeapTimer::_delNode(TimerNode *node)
{
    int last = (int)_heap.size() - 1;
    int idx = node->idx;
    if (idx != last) {
        std::swap(_heap[idx], _heap[last]);
        _heap[idx]->idx = idx;
        if (!_shiftDown(idx)) {
            _shiftUp(idx);
        }
    }
    _heap.pop_back();
    _map.erase(node->id);
    delete node;
}

void MinHeapTimer::ExpireTimer()
{
    if (_heap.empty()) return;
    uint32_t now = current_time();
    do {
        TimerNode* node = _heap.front();
        if (now < node->expire)
            break;
        for (int i = 0; i < _heap.size();  i++)
            std::cout << "touch    idx: " << _heap[i]->idx 
                << " id: " << _heap[i]->id << " expire: "
                << _heap[i]->expire << std::endl;
        if (node->cb) {
            node->cb(node);
        }
        _delNode(node);
    } while(!_heap.empty());
}

bool MinHeapTimer::_shiftDown(int pos){
    int last = (int)_heap.size()-1;
    int idx = pos;
    for (;;) {
        int left = 2 * idx + 1;
        if ((left >= last) || (left < 0)) {
            break;
        }
        int min = left; // left child
        int right = left + 1;
        if (right < last && !_lessThan(left, right)) {
            min = right; // right child
        }
        if (!_lessThan(min, idx)) {
            break;
        }
        std::swap(_heap[idx], _heap[min]);
        _heap[idx]->idx = idx;
        _heap[min]->idx = min;
        idx = min;
    }
    return idx > pos;
}

void MinHeapTimer::_shiftUp(int pos)
{
    for (;;) {
        int parent = (pos - 1) / 2; // parent node
        if (parent == pos || !_lessThan(pos, parent)) {
            break;
        }
        std::swap(_heap[parent], _heap[pos]);
        _heap[parent]->idx = parent;
        _heap[pos]->idx = pos;
        pos = parent;
    }
}


void print_hello(TimerNode *te) {
    std::cout <<  "hello world time = " << te->idx << "\t" << te->id << std::endl;
}

int main() {
    MinHeapTimer mht;
    mht.AddTimer(0, print_hello);
    mht.AddTimer(1000, print_hello);
    mht.AddTimer(7000, print_hello);
    mht.AddTimer(2000, print_hello);
    mht.AddTimer(9000, print_hello);
    mht.AddTimer(10000, print_hello);
    mht.AddTimer(6000, print_hello);
    mht.AddTimer(3000, print_hello);
    for (;;) {
        mht.ExpireTimer();
        usleep(10000);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值