使用跳表实现定时器

大耗子 2021年01月25日 59次浏览

使用跳表实现定时器

  • 使用的是redis中使用的跳表结构,直接从中迁移出来并修改。
  • 通过跳表实现的时间轮,查询第一个数据的时间复杂度就是O(1),插入时间复杂度就 大概率的趋向于O(logn(N))。
  • 定时器本身就是从头部开始取数据,跳表这一数据结构就特别匹配定时器的实现。

文件skiplist.h

#ifndef _MARK_SKIPLIST_
#define _MARK_SKIPLIST_

/* ZSETs use a specialized version of Skiplists */
#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 elements */
#define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */

typedef struct zskiplistNode zskiplistNode;
typedef void (*handler_pt) (zskiplistNode *node);
struct zskiplistNode {
    // sds ele;
    // double score;
    unsigned long score; // 时间戳
    handler_pt handler;
    /* struct zskiplistNode *backward; 从后向前遍历时使用*/
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        /* unsigned long span; 这个存储的level间节点的个数,在定时器中并不需要*/ 
    } level[];
};

typedef struct zskiplist {
    // 添加一个free的函数
    struct zskiplistNode *header/*, *tail 并不需要知道最后一个节点*/;
    int length;
    int level;
} zskiplist;

zskiplist *zslCreate(void);
void zslFree(zskiplist *zsl);
zskiplistNode *zslInsert(zskiplist *zsl, unsigned long score, handler_pt func);
zskiplistNode* zslMin(zskiplist *zsl);
void zslDeleteHead(zskiplist *zsl);
void zslDelete(zskiplist *zsl, zskiplistNode* zn); 

void zslPrint(zskiplist *zsl);
#endif

文件skiplist.c

#include <stdlib.h>
#include <stdio.h>
#include "skiplist.h"

void defaultHandler(zskiplistNode *node) {
}

/* Create a skiplist node with the specified number of levels. */
zskiplistNode *zslCreateNode(int level, unsigned long score, handler_pt func) {
    zskiplistNode *zn =
        malloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));
    zn->score = score;
    zn->handler = func;
    return zn;
}


// 创建跳表
zskiplist *zslCreate(void) {
    int j;
    zskiplist *zsl;

    zsl = malloc(sizeof(*zsl));
    zsl->level = 1;
    zsl->length = 0;
    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,defaultHandler);
    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
        zsl->header->level[j].forward = NULL;
    }
    return zsl;
}

/* Free a whole skiplist. */
void zslFree(zskiplist *zsl) {
    zskiplistNode *node = zsl->header->level[0].forward, *next;

    free(zsl->header);
    while(node) {
        next = node->level[0].forward;
        free(node);
        node = next;
    }
    free(zsl);
}

// 获取随机的层数
int zslRandomLevel(void) {
    int level = 1;
    while ((arc4random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

// 向跳表插入节点
zskiplistNode *zslInsert(zskiplist *zsl, unsigned long score, handler_pt fun){
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
    int i, level;

    x = zsl->header;
	// update记录路径,方便建立节点时候的的前指针指向他
    for (i = zsl->level-1; i >= 0; i--) {
        while (x->level[i].forward &&
                x->level[i].forward->score < score)
        {
            x = x->level[i].forward;
        }
        update[i] = x;
    }
    level = zslRandomLevel();
    printf("zskiplist add node level = %d\n", level);
	// 高度超过了原先最大高度,补充头指向新建的路径
    if (level > zsl->level) {
        for (i = zsl->level; i < level; i++) {
            update[i] = zsl->header;
        }
        zsl->level = level;
    }
    x = zslCreateNode(level,score,func);
    for (i = 0; i < level; i++) {
		// 新建节点接续他的前节点的后续指针
        x->level[i].forward = update[i]->level[i].forward;
		// 前指针指向新建节点
        update[i]->level[i].forward = x;
    }

    zsl->length++;
    return x;
}

// 头结点不存数据,头结点的第一个后续节点为最小
zskiplistNode* zslMin(zskiplist *zsl) {
    zskiplistNode *x;
    x = zsl->header;
    return x->level[0].forward;
}

// 删除跳表头部节点
void zslDeleteHead(zskiplist *zsl) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL];
    zskiplistNode *x = zslMin(zsl);
    if (!x) return;
    int i;
    for (i = zsl->level-1; i >= 0; i--) {
		// 拷贝x的每一层的后续节点
        if (zsl->header->level[i].forward == x) {
            zsl->header->level[i].forward = x->level[i].forward;
        }
    }
	// 计算跳表的最高层
    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)
        zsl->level--;
    zsl->length--;
}


void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {
    int i;
	// 将行走路径上原先指向x的节点指向x的后续节点
    for (i = 0; i < zsl->level; i++) {
        if (update[i]->level[i].forward == x) {
            update[i]->level[i].forward = x->level[i].forward;
        } 
    }
    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)
        zsl->level--;
    zsl->length--;
}

// 删除任一跳表节点
void zslDelete(zskiplist *zsl, zskiplistNode* zn) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
    int i;

    x = zsl->header;
	// update存储行走路径
    for (i = zsl->level-1; i >= 0; i--) {
        while (x->level[i].forward &&
                x->level[i].forward->score < zn->score)
        {
            x = x->level[i].forward;
        }
        update[i] = x;
    }
	// 此时循环刚出来的是欲删除节点的前序节点
    x = x->level[0].forward;
    if (x && zn->score == x->score) {
        zslDeleteNode(zsl, x, update);
        free(x);
    }
}

void zslPrint(zskiplist *zsl) {
    zskiplistNode *x;
    x = zsl->header;
    x = x->level[0].forward;
    printf("start print skiplist level = %d\n", zsl->level);
    int i;
    for (i = 0; i < zsl->length; i++) {
        printf("skiplist ele %d: score = %lu\n", i+1, x->score);
        x = x->level[0].forward;
    }
}

定时器实现

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <time.h>
#if defined(__APPLE__)
#include <AvailabilityMacros.h>
#include <sys/time.h>
#include <mach/task.h>
#include <mach/mach.h>
#endif
#include "skiplist.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;
}

zskiplist *init_timer() {
    return zslCreate();
}

zskiplistNode *add_timer(zskiplist *zsl, uint32_t msec, handler_pt func) {
    msec += current_time();
    printf("add_timer expire at msec = %u\n", msec);
    return zslInsert(zsl, msec, func);
}

void del_timer(zskiplist *zsl, zskiplistNode *zn) {
    zslDelete(zsl, zn);
}

void expire_timer(zskiplist *zsl) {
    zskiplistNode *x;
    uint32_t now = current_time();
    for (;;) {
        x = zslMin(zsl);
        if (!x) break;
        if (x->score > now) break;
        printf("touch timer expire time=%lu, now = %u\n", x->score, now);
        x->handler(x);
        zslDeleteHead(zsl);
    }
}

void print_hello(zskiplistNode *zn) {
    printf("hello world time = %lu\n", zn->score);
}

int main()
{
    zskiplist *zsl = init_timer();
    add_timer(zsl, 3010, print_hello);
    add_timer(zsl, 3004, print_hello);
    zskiplistNode *zn = add_timer(zsl, 3005, print_hello);
    del_timer(zsl, zn);
    add_timer(zsl, 3008, print_hello);
    add_timer(zsl, 3003, print_hello);
    // zslPrint(zsl);
    for (;;) {
        expire_timer(zsl);
        usleep(10000); // 暂用睡眠函数替代阻塞
    }
    return 0;
}