宁波网站建设公司地址,联系方式 响应式网站,莱芜app下载,做黑彩票的网站赚钱1#xff1a;简单基础
定时器的核心知识点#xff0c;对我来说就是获取当前时间和设置回调函数。
简单练习#xff1a;
c语言通过gettimeofday 获取当前时间并进行处理
回调函数的定义#xff08;函数参数有必要适当存储#xff09; typedef void(Timerfunc)(vo…1简单基础
定时器的核心知识点对我来说就是获取当前时间和设置回调函数。
简单练习
c语言通过gettimeofday 获取当前时间并进行处理
回调函数的定义函数参数有必要适当存储 typedef void(Timerfunc)(void p);
1.1 简单源码演示
#include stdio.h
#include sys/time.h
#include unistd.h //sleep
typedef unsigned long int uint64_t;
static uint64_t GetCurrentTime()
{struct timeval tv;gettimeofday(tv, NULL);return tv.tv_sec * 1000 tv.tv_usec / 1000;
}void callback(void * p)
{int * i (int *)p;printf(callback %d\n, *i);
}int main()
{uint64_t mytest 1;printf(%lu \n, GetCurrentTime());sleep(2);struct timeval tv;gettimeofday(tv, NULL);printf(second: %ld\n, tv.tv_sec); // 秒printf(millisecond: %ld\n, tv.tv_sec * 1000 tv.tv_usec / 1000); // 毫秒printf(microsecond: %ld\n, tv.tv_sec * 1000000 tv.tv_usec); // 徽秒sleep(3); // 让程序休眠3秒printf(---------------------sleep 3 second-------------------\n);gettimeofday(tv, NULL);printf(second: %ld\n, tv.tv_sec); // 秒printf(millisecond: %ld\n, tv.tv_sec * 1000 tv.tv_usec / 1000); // 毫秒printf(microsecond: %ld\n, tv.tv_sec * 1000000 tv.tv_usec); // 徽秒
//回调函数的简单定义和使用typedef void(*Timerfunc)(void* p);int func_para 3;Timerfunc m_func callback;void * para (void*)func_para;(*m_func)(para);return 0;
}
1.2 运行结果
rootaliy:/home/leetcode# ./get_time1
1739506868441
second: 1739506870
millisecond: 1739506870441
microsecond: 1739506870441927
---------------------sleep 3 second-------------------
second: 1739506873
millisecond: 1739506873442
microsecond: 1739506873442060
callback 32借助已有的stl容器实现是最方便的
不知道哪里参考的一个代码借助了stl中的一个优先级队列就简单整理一下吧。
回顾好久没写的细节
0优先级队列 priority_queue 支持大堆小堆 自己定义比较函数
1锁和条件变量 条件变量的几种信号等待方式。
2chrono下的相关获取时间的接口需要梳理一下。
3std::function 和lamba需要回顾练习一下
4stl的push时可以直接构造结构体对象task_queue.push(Task{func, exec_time});
5条件变量中的wait 以及wait_until
》 已经有唤醒 wait用条件等待 防止虚假唤醒
》wait_until 接口可以实现等待到特定时间后进行执行系统调用内部定时器实现 会虚假唤醒吗。 和自己代码实现时间差同功能 2.1练习源码
都是C11的东东 需要回顾。
//定时器的简单实现 借助stl容器使用线程进行专门的定时器处理。//容器中保存了定时器的超时时间以及对应的回调函数
#include stdio.h
#include iostream
#include functional
#include mutex
#include thread
#include chrono
#include vector
#include condition_variable
#include atomic
#include queueclass Timer{
private:std::thread worker;std::atomicbool stop;//锁和条件变量std::mutex queue_mutex;std::condition_variable condition;struct Task{std::functionvoid(void) func;//std::chrono::steady_clock 只能增加的单调时钟 std::chrono::time_point表示某一刻的对象//这里时间的相关接口需要参考chronostd::chrono::time_pointstd::chrono::steady_clock exec_time; //为了给wait_until做参数 直接指定bool operator (const Task other) const{return exec_time other.exec_time;}};//优先队列 用task为元素类型 以std::vectorTask 进行存储 按照默认的比较函数进行比较 实现最小堆std::priority_queueTask, std::vectorTask, std::greaterTask task_queue; //底层是堆的结构private:void run(){while(!stop){//这里进行死循环 或者加锁条件变量实现队列中任务的提取if(task_queue.empty()){std::unique_lockstd::mutex lock(queue_mutex);//防止虚假唤醒condition.wait(lock, [this] {return !this-task_queue.empty() || this-stop;});//这里无法访问类的成员变量 如何函数内部或者全局变量 即可以// condition.wait(lock, [] {// return !task_queue.empty() || stop;// });} if(task_queue.empty() || stop){return;}{// auto now std::chrono::steady_clock::now();auto exec_task_time task_queue.top().exec_time; //目标执行的时间std::unique_lockstd::mutex lock(queue_mutex);//注意第二个参数 是当前时间加上最大等待时间 if(condition.wait_until(lock, exec_task_time) std::cv_status::timeout){auto task task_queue.top();task_queue.pop();lock.unlock();task.func(); //这里没有定义参数 可以定义参数为自己}}}}public:Timer():stop(false), worker(Timer::run, this){}~Timer(){ //单例时才把构造函数析构函数设置为私有stop true;condition.notify_all();worker.join();}static inline time_t get_Clock(){// 获取当前时间点auto now std::chrono::steady_clock::now();// 获取从纪元到现在所经过的持续时间auto duration now.time_since_epoch();//转换为毫秒并返回return std::chrono::duration_caststd::chrono::milliseconds(duration).count();}void schedule(const std::functionvoid() func, int delay_ms){auto exec_time std::chrono::steady_clock::now() std::chrono::milliseconds(delay_ms);{std::unique_lockstd::mutex lock(queue_mutex);task_queue.push(Task{func, exec_time}); //注意这里的细节}condition.notify_all();}
};int main()
{Timer timer;printf(now %lu \n, Timer::get_Clock());timer.schedule([](){printf(exec 1000 %lu \n, Timer::get_Clock());}, 1000);timer.schedule([](){printf(exec 3000 %lu \n, Timer::get_Clock());}, 3000);std::this_thread::sleep_for(std::chrono::seconds(5));return 0;
}2.2 运行结果
ootaliy:/home/leetcode# g my_timer1.c -o my_timer -stdc11
rootaliy:/home/leetcode# ./my_timer
now 16306525752
exec 1000 16306526753
exec 3000 16306528753 3总结一些其他遗留
定时器的实现中往往需要数据结构配合。时间戳和回调 需要支持排序 需要方便插入
1红黑树存储数据结构 比如set map mutilset mutilmap 以及nginx下封装的红黑树。
2使用最小堆进行存储。
3跳表有序可以快速插入 参考redis中的跳表源码/时间轮定时器。
3.1时间轮定时器 3.2一个来自别人的demo代码练习 std::vector 模拟数据结构
这样设计有最大超时时间限制吧然后轮询也有精度。
存储的是节点指针增加引用计数实现多次执行类型心跳
//时间轮定时器 采用数组链表链表结合vector的方式存储数据结构 采用轮询的方式处理事件#include unistd.h
#include iostream
#include vector
#include list
using namespace std;#include sys/time.h//同一个指针对象 多次加入只是引用计数增加 执行次数增加。
class CTimerNode {
public:CTimerNode(int fd) : id(fd), ref(0) {}void Offline() {this-ref 0;}//通过引用计数的方式 确定是否销毁该对象 加入时 消费时-- //可能多次加入定时器 bool TryKill() {if (this-ref 0) return true;DecrRef();if (this-ref 0) {cout id is killed down endl;return true;}cout id ref is ref endl;return false;}void IncrRef() {this-ref;}protected:void DecrRef() {this-ref--;}private:int ref;int id;
};const int TW_SIZE 16;
const int EXPIRE 10;
const int TW_MASK TW_SIZE - 1;
static size_t iRealTick 0;
//链表的节点
typedef listCTimerNode* TimeList;
typedef TimeList::iterator TimeListIter;
//用vectorlist 构造时间轮数据结构
typedef vectorTimeList TimeWheel;void AddTimeout(TimeWheel tw, CTimerNode *p) {if (p) {p-IncrRef();TimeList le tw[(iRealTickEXPIRE) TW_MASK]; //基于当前的时间 放入对应的list中 le.push_back(p);}
}// 用来表示delay时间后调用
void AddTimeoutDelay(TimeWheel tw, CTimerNode *p, size_t delay) {if (p) {p-IncrRef();TimeList le tw[(iRealTickEXPIREdelay) TW_MASK];le.push_back(p);}
}//命中
void TimerShift(TimeWheel tw)
{size_t tick iRealTick;iRealTick;TimeList le tw[tick TW_MASK]; //每次向前走一个//循环遍历轮子 消费轮子中的第一个节点对应的list中的所有事件TimeListIter iter le.begin();for (; iter ! le.end();iter) {CTimerNode *p *iter;if (p p-TryKill()) {delete p;}}le.clear();
}static time_t current_time() {time_t t;struct timeval tv;gettimeofday(tv, NULL);t (time_t)tv.tv_sec;return t; //这里返回的是秒
}int main ()
{TimeWheel tw(TW_SIZE);CTimerNode *p new CTimerNode(10001);AddTimeout(tw, p); //加入时间轮定时器中AddTimeoutDelay(tw, p, 5); //对象已经存在 5s后执行对应的回调time_t start current_time();for (;;) {time_t now current_time();//这里以秒为单位 进行依次命中轮询if (now - start 0) {for (int i0; inow-start; i)TimerShift(tw);start now;cout check timer shift iRealTick endl;}usleep(2500); //2500 微妙 2.5ms}return 0;
}
3.3 :demo运行
同一个TimerNode节点只是把指针加入了时间轮中。
每次处理节点时根据引用计数进行判断了。
rootaliy:/home/leetcode# ./wheel_timer
check timer shift 1
check timer shift 2
check timer shift 3
check timer shift 4
check timer shift 5
check timer shift 6
check timer shift 7
check timer shift 8
check timer shift 9
check timer shift 10
10001 ref is 1
check timer shift 11
check timer shift 12
check timer shift 13
check timer shift 14
check timer shift 15
10001 is killed down3.4更复杂的时间轮
linux内核中使用比较复杂的时间轮来进行定时器的处理
参考时钟的时针 分针 秒针多个类似上面的时间轮进行配合采用不同的精度配合实现更复杂的功能只有第一层消费后面的基层都是按层移动到上一层。