高精度定时器

原理:小根堆,以时间为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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值