C++定时器和时间轮

article/2025/7/28 1:05:37

文章目录

  • 定时器
    • 最小堆实现定时器
  • 时间轮
    • 单层级时间轮
    • 多层级时间轮


定时器

有些时候我们需要延迟执行一些功能,比如每10s进行一次数据采集。或者告知用户技能冷却有多少时间,如果我们将执行这些功能的任务交给主线程,就会造成主线程的阻塞。因此我们可以选择一个创建一个子线程,让其检测定时器中的任务,当有任务的时间到了的时候,就去执行这个任务。


最小堆实现定时器

定时器可以由很多种数据结构实现,比如最小堆、红黑树、跳表、甚至数组都可以,其本质都是拿到最小时间的任务,然后取出该任务并执行。
综合实现难度和效率来看,最小堆是最容易实现定时器的数据结构。

最小堆主要有以下接口:

#pragma once#include <vector>
#include <map>
using namespace std;typedef void (*TimerHandler)(struct TimerNode *node);
//获取系统时间,单位是毫秒
static uint32_t current_time()
{uint32_t t;struct timespec ti;clock_gettime(CLOCK_MONOTONIC, &ti);t = (uint32_t)ti.tv_sec * 1000;t += ti.tv_nsec / 1000000;return t;
}struct TimerNode
{//该任务在最小堆中的下标位置int idx = 0;//该任务是第几号任务int id = 0;//几毫秒后执行该任务unsigned int expire = 0;//回调函数TimerHandler cb = NULL;
};class MinHeapTimer
{
public:MinHeapTimer(){_heap.clear();_map.clear();}int Count();//加入任务,expire为该任务的失效时间,expire过后就要执行回调函数cbint AddTimer(uint32_t expire, TimerHandler cb);//删除一个任务bool DelTimer(int id);//获取一个任务void ExpireTimer();private://用于比较两个任务的过期时间bool _compare(int lhs, int rhs);//向下调整算法,每次删除一个节点就要向下调整void _shiftDown(int parent);//向上调整算法,每添加一个数都要调用向上调整算法,保证根节点为最小节点void _shiftUp(int child);//删除的子函数void _delNode(TimerNode *node);void resign(int pos);private://数组中存储任务节点vector<TimerNode *> _heap;//存储值和响应节点的映射关系map<int, TimerNode *> _map;//任务的个数,注意不是_heap的sizeint _count = 0;
};

具体的实现以及测试为:

#pragma once#include <vector>
#include <map>
using namespace std;typedef void (*TimerHandler)(struct TimerNode *node);
//获取系统时间,单位是毫秒
static uint32_t current_time()
{uint32_t t;struct timespec ti;clock_gettime(CLOCK_MONOTONIC, &ti);t = (uint32_t)ti.tv_sec * 1000;t += ti.tv_nsec / 1000000;return t;
}struct TimerNode
{//该任务在最小堆中的下标位置int idx = 0;//该任务是第几号任务int id = 0;unsigned int expire = 0;//回调函数TimerHandler cb = NULL;
};class MinHeapTimer
{
public:MinHeapTimer(){_heap.clear();_map.clear();}int Count(){return ++_count;}//加入任务,expire为该任务的失效时间,expire过后就要执行回调函数cbint 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 DelTimer(int id){auto iter = _map.find(id);if (iter == _map.end())return false;_delNode(iter->second);return true;}//获取一个任务void ExpireTimer(){if (_heap.empty()){return;}//获取当前时间uint32_t now = current_time();while (!_heap.empty()){//获取最近的一个任务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);}}private://用于比较两个任务的过期时间bool _compare(int lhs, int rhs){return _heap[lhs]->expire < _heap[rhs]->expire;}//向下调整算法,每次删除一个节点就要向下调整void _shiftDown(int parent){int child = parent * 2 + 1;while (child < _heap.size() - 1){if (child + 1 < _heap.size() - 1 && !_compare(child, child + 1)){child++;}if (!_compare(parent, child)){std::swap(_heap[parent], _heap[child]);_heap[parent]->idx = parent;_heap[child]->idx = child;parent = child;child = parent * 2 + 1;}else{break;}}}//向上调整算法,每添加一个数都要调用向上调整算法,保证根节点为最小节点void _shiftUp(int child){int parent = (child - 1) / 2;while (child > 0){if (!_compare(parent, child)){std::swap(_heap[parent], _heap[child]);_heap[parent]->idx = parent;_heap[child]->idx = child;child = parent;parent = (child - 1) / 2;}else{break;}}}//删除的子函数void _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;resign(idx);}_heap.pop_back();_map.erase(node->id);delete node;}void resign(int pos){//向上调整和向下调整只会发生一个_shiftDown(pos);_shiftUp(pos);}private://数组中存储任务节点vector<TimerNode *> _heap;//存储值和响应节点的映射关系map<int, TimerNode *> _map;//任务的个数,注意不是_heap的sizeint _count = 0;
};
#include <time.h>
#include <unistd.h>
#include <iostream>#include "minheap.h"void print_hello(TimerNode *te)
{std::cout << "hello world time = " << te->idx << "\t" << te->id << "\t" << current_time() << 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);while (1){mht.ExpireTimer();// usleep(10000);sleep(1);}return 0;
}

在这里插入图片描述


时间轮

上面的定时器在任务数量很多时,效率会很低,因为我们需要用最小堆来维护这些任务,并且每删除一个任务,都要进行调整。其主要原因是我们不知道其他任务什么时候执行,所以我们只能进行调整,将最近的任务放到堆顶。

如果我们能够向哈希桶那样,将要执行的任务形成链表,挂到要执行的位置,当时间走到那个位置的时候,就执行这些任务,效率岂不是更高?
在这里插入图片描述

单层级时间轮

客户端每 5 秒钟发送⼼跳包;服务端若 10 秒内没收到⼼跳数据,则清除连接。

考虑到正常情况下 5 秒钟发送⼀次⼼跳包,10 秒才检测⼀次,如下图到索引为 10 的时候并不能踢掉连接;所以需要每收到⼀次⼼跳包则 used++ ,每检测⼀次 used-- ;当检测到used == 0 则踢掉连接;
在这里插入图片描述

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>#include <sys/time.h>
#include <time.h>#define MAX_CONN ((1 << 16) - 1)//连接的节点,用来记录心跳包发送的次数
typedef struct conn_node
{struct conn_node *next;//引用计数的次数,当used==0,相当于自动销毁uint8_t used;int id;
} conn_node_t;
//用数组记录所有的连接
static conn_node_t nodes[MAX_CONN] = {0};
static uint32_t iter = 0;//获取一个空的连接节点
conn_node_t *get_node()
{iter++;while (nodes[iter & MAX_CONN].used > 0){iter++;}return &nodes[iter];
}//哈希桶的个数
#define TW_SIZE 16
//检测心跳包的延迟时间,由于哈希桶的个数有限,所以心跳包发送时间不能够超过6
#define EXPIRE 10
//取余操作
#define TW_MASK (TW_SIZE - 1)
static uint32_t tick = 0;
//哈希桶
typedef struct link_list
{conn_node_t head;//一个tail,能够进行快速插入conn_node_t *tail;
} link_list_t;
//添加连接
void add_conn(link_list_t *tw, conn_node_t *node, int delay)
{//获取对应的哈希桶link_list_t *list = &tw[(tick + EXPIRE + delay) & TW_MASK];list->tail->next = node;list->tail = node;node->next = NULL;node->used++;
}
//清楚这个哈希桶
void link_clear(link_list_t *list)
{list->head.next = NULL;list->tail = &(list->head);
}
//检测哈希桶
void check_conn(link_list_t *tw)
{int32_t itick = tick;tick++;//取到对应哈希桶的链表link_list_t *list = &tw[itick & TW_MASK];//检测哈希桶对应的链表conn_node_t *current = list->head.next;while (current){conn_node_t *temp = current;current = current->next;temp->used--;if (temp->used == 0){printf("连接:%d 断开\n", temp->id);temp->next = NULL;continue;}printf("这个链接:%d 心跳包还剩:%d个需要检测\n", temp->id, temp->used);}link_clear(list);
}
//获取时间,单位是s
static time_t current_time()
{time_t t;struct timespec ti;clock_gettime(CLOCK_MONOTONIC, &ti);t = (time_t)ti.tv_sec;return t;
}int main()
{memset(nodes, 0, MAX_CONN * sizeof(conn_node_t));// init link listlink_list_t tw[TW_SIZE];memset(tw, 0, TW_SIZE * sizeof(link_list_t));for (int i = 0; i < TW_SIZE; i++){link_clear(&tw[i]);}// 第一个连接对应的心跳包,在0和5时进行发送//所以会在10s和15s时进行检测,15s时把该连接断开{conn_node_t *node = get_node();node->id = 10001;add_conn(tw, node, 0);add_conn(tw, node, 5);}//第二个连接发送的心跳包,在第10s时进行检测{conn_node_t *node = get_node();node->id = 10002;add_conn(tw, node, 0);}//第二个连接发送的心跳包,在第13s时检测{conn_node_t *node = get_node();node->id = 10003;add_conn(tw, node, 3);}time_t start = current_time();while (1){time_t now = current_time();if (now - start > 0){for (int i = 0; i < now - start; i++)check_conn(tw);start = now;printf("在第%d秒时检测,此时机器时间:%d\n", tick, now);}}return 0;
}

在这里插入图片描述

上面的时间轮受哈希桶大小和延迟10s收到心跳包的影响,只能在[0,6]秒内发送数据心跳包,如果想要延迟长时间,则需要扩大哈希桶的大小。

如果我们不发送心跳包,而是改成在若干秒后执行一个任务,比如50s后执行任务,但哈希桶大小只有16,我们可以在任务节点中增加一个参数round,用来记录需要走多少遍哈希桶,比如50s,对应到大小为16的哈希桶则round=3,idx=2。
不过这样做,在check_conn取任务时就不能够把整个链表都取出来,而是需要取出round==0的任务。

typedef struct node
{struct conn_node *next;//需要走多少轮哈希桶,当round==0时,则说明需要执行这个任务int round;int id;
} node_t;

这样做能解决时间轮刻度范围过大造成的空间浪费,但是却带来了另一个问题:时间轮每次都需要遍历任务列表,耗时增加,当时间轮刻度粒度很小(秒级甚至毫秒级),任务列表又特别长时,这种遍历的办法是不可接受的。

多层级时间轮

参照时钟表盘的运转规律,可以将定时任务根据触发的紧急程度,分布到不同层级的时间轮中;
假设时间精度为 10ms ;在第 1 层级每 10ms 移动⼀格;每移动⼀格执⾏该格⼦当中所有的定时任务;
当第 1 层指针从 255 格开始移动,此时层级 2 移动⼀格;层级 2 移动⼀格的⾏为定义为,将该格当中的定时任务重新映射到层级 1 当中;同理,层级 2 当中从 63 格开始移动,层级 3 格⼦中的定时任务重新映射到层级 2 ; 以此类推层级 4 往层级 3 映射,层级 5 往层级 4 映射。
只有任务在第一层时才会被执行,其他层都是将任务重新映射到上一层。
在这里插入图片描述

  • timewheel.h:
#pragma once#include <stdint.h>#define TIME_NEAR_SHIFT 8
#define TIME_NEAR (1 << TIME_NEAR_SHIFT)
#define TIME_LEVEL_SHIFT 6
#define TIME_LEVEL (1 << TIME_LEVEL_SHIFT)
#define TIME_NEAR_MASK (TIME_NEAR - 1)
#define TIME_LEVEL_MASK (TIME_LEVEL - 1)typedef struct timer_node timer_node_t;
typedef void (*handler_pt)(struct timer_node *node);struct timer_node
{struct timer_node *next;uint32_t expire;	 //时间handler_pt callback; //回调函数uint8_t cancel;		 //是否删除int id;				 // 此时携带参数
};timer_node_t *add_timer(int time, handler_pt func, int threadid);void expire_timer(void);void del_timer(timer_node_t *node);void init_timer(void);void clear_timer();
  • spinlock.h:
#pragma oncestruct spinlock
{int lock;
};void spinlock_init(struct spinlock *lock)
{lock->lock = 0;
}void spinlock_lock(struct spinlock *lock)
{while (__sync_lock_test_and_set(&lock->lock, 1)){}
}int spinlock_trylock(struct spinlock *lock)
{return __sync_lock_test_and_set(&lock->lock, 1) == 0;
}void spinlock_unlock(struct spinlock *lock)
{__sync_lock_release(&lock->lock);
}void spinlock_destroy(struct spinlock *lock)
{(void)lock;
}
  • timewheel.cpp:
#include "spinlock.h"
#include "timewheel.h"
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <time.h>typedef struct link_list
{timer_node_t head;timer_node_t *tail;
} link_list_t;//多级时间轮
typedef struct timer
{//第一级link_list_t near[TIME_NEAR];// 2-4级link_list_t t[4][TIME_LEVEL];struct spinlock lock;uint32_t time;uint64_t current;uint64_t current_point;
} s_timer_t;static s_timer_t *TI = NULL;timer_node_t *link_clear(link_list_t *list)
{timer_node_t *ret = list->head.next;list->head.next = 0;list->tail = &(list->head);return ret;
}
//链接一个节点
void link(link_list_t *list, timer_node_t *node)
{list->tail->next = node;list->tail = node;node->next = 0;
}void add_node(s_timer_t *T, timer_node_t *node)
{uint32_t time = node->expire;uint32_t current_time = T->time;uint32_t msec = time - current_time;//根据时间if (msec < TIME_NEAR){ //[0, 0x100)link(&T->near[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);}
}
//增加事件
timer_node_t *add_timer(int time, handler_pt func, int threadid)
{timer_node_t *node = (timer_node_t *)malloc(sizeof(*node));spinlock_lock(&TI->lock);node->expire = time + TI->time; // 每10ms加1 0node->callback = func;node->id = threadid;if (time <= 0){node->callback(node);free(node);spinlock_unlock(&TI->lock);return NULL;}add_node(TI, node);spinlock_unlock(&TI->lock);return node;
}void move_list(s_timer_t *T, int level, int idx)
{timer_node_t *current = link_clear(&T->t[level][idx]);while (current){timer_node_t *temp = current->next;add_node(T, current);current = temp;}
}void timer_shift(s_timer_t *T)
{int mask = TIME_NEAR;uint32_t ct = ++T->time;if (ct == 0){move_list(T, 3, 0);}else{// ct / 256uint32_t time = ct >> TIME_NEAR_SHIFT;int i = 0;// ct % 256 == 0while ((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;}}
}void dispatch_list(timer_node_t *current)
{do{timer_node_t *temp = current;current = current->next;if (temp->cancel == 0)temp->callback(temp);free(temp);} while (current);
}void timer_execute(s_timer_t *T)
{int idx = T->time & TIME_NEAR_MASK;while (T->near[idx].head.next){timer_node_t *current = link_clear(&T->near[idx]);spinlock_unlock(&T->lock);dispatch_list(current);spinlock_lock(&T->lock);}
}void timer_update(s_timer_t *T)
{spinlock_lock(&T->lock);timer_execute(T);timer_shift(T);timer_execute(T);spinlock_unlock(&T->lock);
}void del_timer(timer_node_t *node)
{node->cancel = 1;
}s_timer_t *timer_create_timer()
{s_timer_t *r = (s_timer_t *)malloc(sizeof(s_timer_t));memset(r, 0, sizeof(*r));int i, j;for (i = 0; i < TIME_NEAR; i++){link_clear(&r->near[i]);}for (i = 0; i < 4; i++){for (j = 0; j < TIME_LEVEL; j++){link_clear(&r->t[i][j]);}}spinlock_init(&r->lock);r->current = 0;return r;
}uint64_t gettime()
{uint64_t t;struct timespec ti;clock_gettime(CLOCK_MONOTONIC, &ti);t = (uint64_t)ti.tv_sec * 100;t += ti.tv_nsec / 10000000;return t;
}void expire_timer(void)
{uint64_t cp = gettime();if (cp != TI->current_point){uint32_t diff = (uint32_t)(cp - TI->current_point);TI->current_point = cp;int i;for (i = 0; i < diff; i++){timer_update(TI);}}
}void init_timer(void)
{TI = timer_create_timer();TI->current_point = gettime();
}void clear_timer()
{int i, j;for (i = 0; i < TIME_NEAR; i++){link_list_t *list = &TI->near[i];timer_node_t *current = list->head.next;while (current){timer_node_t *temp = current;current = current->next;free(temp);}link_clear(&TI->near[i]);}for (i = 0; i < 4; i++){for (j = 0; j < TIME_LEVEL; j++){link_list_t *list = &TI->t[i][j];timer_node_t *current = list->head.next;while (current){timer_node_t *temp = current;current = current->next;free(temp);}link_clear(&TI->t[i][j]);}}
}
  • tw-timer.cpp:
#include <stdio.h>
#include <unistd.h>#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include "timewheel.h"struct context
{int quit;int thread;
};struct thread_param
{struct context *ctx;int id;
};static struct context ctx = {0};void do_timer(timer_node_t *node)
{printf("timer expired:%d - thread-id:%d\n", node->expire, node->id);
}void *thread_worker(void *p)
{struct thread_param *tp = (struct thread_param *)p;int id = tp->id;struct context *ctx = tp->ctx;while (!ctx->quit){int expire = rand() % 200;add_timer(expire, do_timer, id);usleep(expire * (10 - 1) * 1000);}printf("thread_worker:%d exit!\n", id);return NULL;
}void do_quit(timer_node_t *node)
{ctx.quit = 1;
}int main()
{srand(time(NULL));ctx.thread = 8;pthread_t pid[ctx.thread];init_timer();add_timer(6000, do_quit, 100);struct thread_param task_thread_p[ctx.thread];int i;for (i = 0; i < ctx.thread; i++){task_thread_p[i].id = i;task_thread_p[i].ctx = &ctx;if (pthread_create(&pid[i], NULL, thread_worker, &task_thread_p[i])){fprintf(stderr, "create thread failed\n");exit(1);}}while (!ctx.quit){expire_timer();usleep(2500);}clear_timer();for (i = 0; i < ctx.thread; i++){pthread_join(pid[i], NULL);}printf("all thread is closed\n");return 0;
}

在这里插入图片描述


http://chatgpt.dhexx.cn/article/I8Rc9xSN.shtml

相关文章

简单描述时间轮

时间轮 作用 也是用来作定时器触发任务&#xff0c;只是他更高效&#xff0c;时间复杂度为O(1)。 运行原理 为了方便理解我们参考钟表&#xff0c;它分为3个层次&#xff1a;时、分、秒&#xff0c;只有秒针在运动&#xff0c;走动一格时间为1秒&#xff0c;走一圈为1分钟&…

再谈时间轮

时间轮很早前就很流行了&#xff0c;在很多优秀开源框架中都有用到&#xff0c;像kafka、netty。也算是现在工程师基本都了解的一个知识储备了。有幸在工作中造过两次轮子&#xff0c;所以今天聊聊时间轮。 时间轮是一种高性能定时器。 时间轮&#xff0c;顾名思义&#xff0c…

时间轮原理及其在框架中的应用

一、时间轮简介 1.1 为什么要使用时间轮 在平时开发中&#xff0c;经常会与定时任务打交道。下面举几个定时任务处理的例子。 1&#xff09;心跳检测。在Dubbo中&#xff0c;需要有心跳机制来维持Consumer与Provider的长连接&#xff0c;默认的心跳间隔是60s。当Provider在3…

时间轮分析

背景 在需要完成重试机制或者心跳机制一类的业务&#xff0c;除了使用excutors做定时任务。还可以使用时间轮完成这类需求 分析 使用dubbo中的心跳重试作为案例分析时间轮的使用 源码分析 在HeaderExchangeClient启动的时候调用startHeartBeatTask()开启心跳任务&#xff0…

Netty时间轮

概述 时间轮是一个高性能&#xff0c;低消耗的数据结构&#xff0c;它适合用非准实时&#xff0c;延迟的短平快任务&#xff0c;例如心跳检测。在netty和kafka中都有使用。 比如Netty动辄管理100w的连接&#xff0c;每一个连接都会有很多超时任务。比如发送超时、心跳检测间隔等…

高性能定时器时间轮的实现

时间轮的概念 关于定时器有很多种&#xff0c;有基于升序的定时器时间链表&#xff0c;但是这种链表存在效率的不足&#xff0c;就是当插入定时器的时候时间复杂度是O(n).今天&#xff0c;我们来认识一下高性能定时器时间轮。 如上图所示&#xff0c;就是一个时间轮的基本轮廓…

深度剖析 -- 时间轮算法(TimingWheel)是如何实现的?

前言 时间轮的应用场景很多&#xff0c;在 Netty、Akka、Quartz、ZooKeeper 、Kafka等组件中都存在时间轮的踪影。我们下面讲解的时间轮的实现以JRaft中的为例子进行讲解&#xff0c;因为JRaft这部分的代码是参考Netty的&#xff0c;所以大家也可以去Netty中去寻找源码实现。 …

Kafka原理--时间轮(延时操作)

原文网址&#xff1a;Kafka原理--时间轮(延时操作)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Kafka的时间轮的原理。 Kafka没有延迟队列功能供用户使用&#xff0c;本文介绍的延时操作仅仅是Kafka内部的使用&#xff0c;用户无法使用。 Kafka延时操作 Kafka中存在大量的…

时间轮算法HashedWheelTimer

文章目录 一.HashedWheelTimer是什么?二.能干什么?为什么需要这个东西?优点适用场景 三.怎么用?使用步骤1.引入pom2.使用举例 四.时间轮原理五.使用注意点1.一个HashedWheelTimer对象只有一个worker线程2.每次添加的任务只会执行一次3.时间轮的参数非常重要4.所有的任务都是…

时间轮-理论篇

1. 引言 定时任务再开发过程中无处不在&#xff0c;定时发送消息&#xff0c;定时更新数据库表的状态&#xff0c;Linux系统定时执行脚本等等。这些操作都离不开定时任务&#xff0c;那么这些定时任务是怎么实现的是否又去想过。如果让开发者自己去实现一个定时任务又该怎么实…

定时任务的实现原理:时间轮算法

文章目录 前言时间轮定时使用方式时间轮定时内部原理时间轮定时源码剖析构造方法添加任务工作线程启动工作线程run方法指针跳动将队列任务放入时间轮中链表任务遍历定时任务执行 前言 最近在思考实现定时任务的几种方式&#xff0c;比如 quartz&#xff0c;delay queue&#xf…

时间轮-Java实现篇

在前面的文章《时间轮-理论篇》讲了时间轮的一些理论知识&#xff0c;然后根据理论知识。我们自己来实现一个简单的时间轮。 1. 理论抽象 将时间轮的理论进行抽象&#xff0c;主要有两个方面&#xff1a; 时间轮的转动每一个时间间隔任务的处理&#xff0c;从时间间隔的Buke…

时间轮timewheel算法

时间轮是个不太常见&#xff0c;但在部分场景有较高使用价值的工具。 时间轮常用于延时任务&#xff0c;在Netty、akka、Quartz、Zookeeper等高性能组件中都存在时间轮定时器的踪影。 从定时任务说起 自然界中定时任务无处不在&#xff0c;太阳每天东升西落&#xff0c;候鸟的…

定时器实现原理——时间轮

时间轮 时间轮算法是通过一个时间轮去维护定时任务&#xff0c;按照一定的时间单位对时间轮进行划分刻度。然后根据任务延时计算任务落在该时间轮的第几个刻度上&#xff0c;如果任务时长超出了刻度数量&#xff0c;则需要增加一个参数记录时间轮需要转动的圈数。 简单时间轮…

时间轮算法

一、时间轮算法 1. 时间轮的基本概念 Kafka中存在大量的延时操作&#xff0c;如延时生产、延时消费等&#xff1b;而JDK中自带的 Timer 和 DelayQueue 的插入和删除操作的平均复杂度为 O&#xff08;nlogn&#xff09;&#xff0c;无法满足 Kafka 的高性能要求&#xff0c;因…

时间轮算法(TimingWheel)

时间轮算法的应用非常广泛&#xff0c;在 Dubbo、Netty、Kafka、ZooKeeper、Quartz 的组件中都有时间轮思想的应用&#xff0c;甚至在 Linux 内核中都有用到。 其思想理论基础可以参考论文&#xff1a;http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pd…

那些惊艳的算法们(三)—— 时间轮

从定时任务说起 自然界中定时任务无处不在&#xff0c;太阳每天东升西落&#xff0c;候鸟的迁徙&#xff0c;树木的年轮&#xff0c;人们每天按时上班&#xff0c;每个月按时发工资、交房租&#xff0c;四季轮换&#xff0c;潮涨潮落&#xff0c;等等&#xff0c;从某种意义上…

1、时间轮

一、什么是时间轮&#xff1f; 作为一个粗人&#xff0c;咱不扯什么高级的词汇&#xff0c;直接上图&#xff1a; 上面是一张时间轮的示意图&#xff0c;可以看到&#xff0c;这个时间轮就像一个钟表一样&#xff0c;它有刻度&#xff0c;图中画了9个格子&#xff0c;每个格子…

浅谈时间轮算法

浅谈时间轮算法 基于队列的定时任务执行模型缺陷 在计算机世界中&#xff0c;只有待解决的问题变得大规模后&#xff0c;算法的价值才能够最大化的体现。时间轮算法可以将插入和删除操作的时间复杂度都降为 O(1)&#xff0c;在大规模问题下还能够达到非常好的运行效果。 如果…

时间轮(TimingWheel)算法总结

通过阅读篇文章您可以很容易理解平时所使用的开源框架是如何进行任务调度的。而且对于以后业务上碰到需要做时间任务调度的需求&#xff0c;也可以尝试着用实践论算法去实现。 一、时间轮的应用 其实早在1987年&#xff0c;时间轮算法的论文已经发布。论文名称&#xff1a;Hash…