原理:小根堆,以时间为key
MiniHeap小根堆实现
#pragma once
#include <vector>
#include <algorithm>
//算法参考
//https://www.cnblogs.com/WindSun/p/11444446.html
/*一般都用数组来表示堆
由于堆存储在下标从0开始计数的数组中,因此,在堆中给定下标为i的结点时:
(1)如果i = 0,结点i是根结点,无父结点;否则结点i的父结点为结点(i - 1) / 2;
(2)如果2i + 1 > n - 1,则结点i无左子女;否则结点i的左子女为结点2i + 1;
(3)如果2i + 2 > n - 1,则结点i无右子女;否则结点i的右子女为结点2i + 2。*/
template <typename T>
class MiniHeap{
private:
std::vector<T> _vdata;
public:
MiniHeap(size_t deault_size = 32)
{
_vdata.reserve(deault_size);
}
virtual ~MiniHeap(){
}
void add(const T &val)
{
//添加采取上浮法
_vdata.push_back(val);
mini_heap_filterup();
}
void remove(const T&val)
{
//删除采取下浮法
std::vector<T>::iterator itfind = std::find_if(_vdata.begin(), _vdata.end(), [&val](const T&v){
return v == val;
});
if (itfind != _vdata.end())
{
int pos = std::distance(_vdata.begin(), itfind);
*itfind = *(_vdata.end() - 1);
_vdata.erase(_vdata.end() - 1);
mini_heap_filterdown(pos);
}
}
bool getRootVal( T &val)
{
if (_vdata.empty())
{
return false;
}
val = _vdata[0];
return true;
}
private:
//小根堆上浮算法 parent <= child
void mini_heap_filterup()
{
size_t cur_index = _vdata.size() - 1;
if (cur_index < 1)
{
return;
}
size_t parent_index = (cur_index - 1) / 2;
//如果parent > child,则上浮,交换位置
while (_vdata[cur_index] < _vdata[parent_index])
{
const T val = _vdata[cur_index];
_vdata[cur_index] = _vdata[parent_index];
_vdata[parent_index] = val;
cur_index = parent_index;
if (cur_index < 1)
{
return;
}
parent_index = (cur_index - 1) / 2;
}
}
//小根堆下浮法 parent <= child
void mini_heap_filterdown(size_t pos)
{
size_t parent_pos = pos;
while (true)
{
size_t left_child_pos = 2 * parent_pos + 1;
size_t right_child_pos = 2 * parent_pos + 2;
size_t childe_pos = -1;
//如果左右节点都存在
if (left_child_pos < _vdata.size())
{
childe_pos = left_child_pos;
}
if (right_child_pos < _vdata.size())
{
if (!(_vdata[left_child_pos] < _vdata[right_child_pos]))
{
childe_pos = right_child_pos;
}
}
if (childe_pos == -1)
{
break;
}
const T child_val = _vdata[childe_pos];
if (!(child_val < _vdata[parent_pos]))
{
break;
}
else
{
_vdata[childe_pos] = _vdata[parent_pos];
_vdata[parent_pos] = child_val;
}
parent_pos = childe_pos;
}
}
};
定时器实现 XTimer.h
#pragma once
#include <thread>
#include<functional>
#include <mutex>
#include <Windows.h>
#include <map>
#include "MiniHeap.h"
/*
根据小根堆实现的毫秒级定时器,异步触发,需要注意回调的多线程安全
*/
struct TimerNode
{
TimerNode()
{
initTickFreq();
_timer_id = -1;
_elapse = 1000;
arg = nullptr;
_timer_call = nullptr;
_tick = getTick_ms();
}
int _timer_id;
int _elapse;
void *arg;
std::function<void(int,void *)> _timer_call;
friend bool operator <(const TimerNode& node0, const TimerNode& node1)
{
return node0._tick < node1._tick;
}
friend bool operator == (const TimerNode& node0, const TimerNode& node1)
{
return node0._timer_id == node1._timer_id;
}
void update_tick()
{
_tick = getTick_ms() + _elapse;
}
//剩余时间
__int64 reset_timeout()
{
return _tick - getTick_ms();
}
static ULONGLONG getTick_ms()
{
if (!m_issupport_high)
{
return GetTickCount64();
}
else
{
LARGE_INTEGER lg;
QueryPerformanceCounter(&lg);
return lg.QuadPart*1000.0 / m_tickfreq;
}
}
private:
static void initTickFreq()
{
LARGE_INTEGER lg;
if (!m_issupport_high && QueryPerformanceFrequency(&lg))
{
m_issupport_high = true;
m_tickfreq = lg.QuadPart;
}
}
private:
unsigned long long _tick;
static bool m_issupport_high;//是否支持高精度
static LONGLONG m_tickfreq;//tick每秒钟的频率精度,默认1000,即ms
};
LONGLONG TimerNode::m_tickfreq = 1000;
bool TimerNode::m_issupport_high = false;
class CXTimer
{
public:
CXTimer()
{
m_is_exit = false;
m_thread = std::thread(std::bind(&CXTimer::_timer_thread,this));
}
~CXTimer()
{
m_is_exit = true;
m_thread.join();
}
void setTimer(int timer_id, int elapse_ms,void *arg, std::function<void(int, void *)> call_back)
{
if (elapse_ms <= 0)
{
return;
}
TimerNode node;
node._timer_call = call_back;
node._timer_id = timer_id;
node.arg = arg;
node._elapse = elapse_ms;
node.update_tick();
std::lock_guard<std::mutex> lk(m_tx);
m_heap_node.remove(node);
m_heap_node.add(node);
}
void killTimer(int timer_id)
{
TimerNode node;
node._timer_id = timer_id;
std::lock_guard<std::mutex> lk(m_tx);
m_heap_node.remove(node);
}
void _timer_thread()
{
while (!m_is_exit)
{
TimerNode timer_node;
{
std::lock_guard<std::mutex> lk(m_tx);
m_heap_node.getRootVal(timer_node);
}
__int64 reset_time = timer_node.reset_timeout();
if (timer_node._timer_id != -1 && reset_time <= 0)
{
{
std::lock_guard<std::mutex> lk(m_tx);
m_heap_node.remove(timer_node);
timer_node.update_tick();
m_heap_node.add(timer_node);
}
//由于是单线程,并且存在回调耗时,所以对于超高精度的定时器,应该使用线程池,否则实际耗时会 > 理论耗时
timer_node._timer_call(timer_node._timer_id, timer_node.arg);
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(timer_node._timer_id == -1 ? 1 : reset_time));
}
}
}
private:
private:
MiniHeap<TimerNode> m_heap_node;
std::thread m_thread;
bool m_is_exit;
std::mutex m_tx;
};
代码测试
void timer_call(int timerId, void *arg)
{
}
CXTimer xtimer;
xtimer.setTimer(1, 100, &ac0, timer_call);