一、小跟堆的原理
参见文章:
二、小根堆的实现:
#define MIN_HEAP_ERROR 0
#define MIN_HEAP_OK 1
#define MIN_HEAP_INVALID_IDX -1
#define MIN_HEAP_NAME_LEN 32
/* 小根堆节点,嵌套在宿主结构体里面 */
typedef struct minHeapEntryStruct
{
INT32 minHeapIndex;
} minHeapEntry;
/* 小根堆管理节点 */
typedef struct minHeapStruct
{
void **data; /* 指向小根堆宿主结构体指针数组 */
INT32 currHeapCnt; /* 当前小根堆里面的个数 */
INT32 heapRlimit; /* 小根堆容量,如果容量不足,会根据这个参数扩大2倍 */
/* 宿主结构构建小根堆关键字大小比较,用于调整小根堆,elem1 > elem2 返回1,否则返回0 */
INT32 (*elemCompare)(void *elem1, void *elem2);
/* 设置嵌套在宿主结构里面的minHeapEntry的值 */
void (*elemSetEntry)(void *elem, minHeapEntry *entry);
char name[MIN_HEAP_NAME_LEN + 1];
} minHeap;
static inline void minHeapElemInit(minHeapEntry *e)
{
e->minHeapIndex = MIN_HEAP_INVALID_IDX;
return;
}
static inline INT32 minHeapEmpty(minHeap *s)
{
return s->currHeapCnt <= 0;
}
static inline UINT32 minHeapSize(minHeap *s)
{
return s->currHeapCnt;
}
static inline void *minHeapTop(minHeap *s)
{
return s->currHeapCnt ? *s->data : NULL;
}
static inline INT32 minHeapElemIsTop(const minHeapEntry *e)
{
return e->minHeapIndex == 0;
}
static inline void minHeapShiftUp(minHeap *s, INT32 heapIndex, void *e)
{
minHeapEntry mhEntry;
INT32 parent = (heapIndex - 1) / 2;
while (heapIndex && s->elemCompare(s->data[parent], e))
{
s->data[heapIndex] = s->data[parent]; /* parent 向下移动 */
mhEntry.minHeapIndex = heapIndex;
s->elemSetEntry(s->data[heapIndex], &mhEntry); /* 设置parent 的index */
heapIndex = parent;
parent = (heapIndex - 1) / 2;
}
s->data[heapIndex] = e; /* 找到了当前插入节点的位置,并赋值 */
mhEntry.minHeapIndex = heapIndex;
s->elemSetEntry(s->data[heapIndex], &mhEntry); /* 设置当前节点的index */
return;
}
static inline void minHeapShiftDown(minHeap *s, INT32 heapIndex, void *e)
{
minHeapEntry mhEntry;
INT32 minChild = 2 * (heapIndex + 1);
while (minChild <= s->currHeapCnt)
{
/* 查找最小的子节点 */
if (minChild == s->currHeapCnt
|| s->elemCompare(s->data[minChild], s->data[minChild - 1]))
{
minChild -= 1;
}
if (s->elemCompare(s->data[minChild], e))
{
break;
}
/* 子节点比e小,则把子节点向上移动 */
s->data[heapIndex] = s->data[minChild]; /* minChild 向上移动 */
mhEntry.minHeapIndex = heapIndex;
s->elemSetEntry(s->data[heapIndex], &mhEntry); /* 设置minChild 的index */
heapIndex = minChild;
minChild = 2 * (heapIndex + 1);
}
s->data[heapIndex] = e; /* 找到了当前插入节点的位置,并赋值 */
mhEntry.minHeapIndex = heapIndex;
s->elemSetEntry(s->data[heapIndex], &mhEntry); /* 设置当前节点的index */
return;
}
/* 如果当前小根堆空间不足,需要调整小根堆的大小 */
static inline INT32 minHeapReserve(minHeap *s, UINT32 heapCnt)
{
UINT32 currHeapRlimit;
void **reData;
if (s->heapRlimit < heapCnt)
{
currHeapRlimit = s->heapRlimit << 2;
reData = (void **)realloc(s->data, currHeapRlimit * sizeof(*reData));
if (reData == NULL)
{
return MIN_HEAP_ERROR;
}
s->data = reData;
s->heapRlimit = currHeapRlimit;
}
return MIN_HEAP_OK;
}
static inline INT32 minHeapCheckInminHeap(minHeap *s, void *e)
{
INT32 i;
for (i = 1; i < s->currHeapCnt; i++)
{
if (s->data[i] == e)
{
return 2;
}
}
return 1;
}
/*
* 插入一个节点,此时放在数组的当前第一个空闲的位子,然后执行小根堆的
* UP 函数,往上调整小根堆
*/
static inline INT32 minHeapInsert(minHeap *s, void *e)
{
if (minHeapReserve(s, s->currHeapCnt + 1) == MIN_HEAP_ERROR)
{
return MIN_HEAP_ERROR;
}
minHeapShiftUp(s, s->currHeapCnt++, e);
return MIN_HEAP_OK;
}
/*
* 从小根堆根部弹出数据,然后向下调整小根堆
* e = minHeapPop
* while (2)
* {
* e = minHeapPop
* if (e == NULL)
* {
* break;
* }
* }
* 此时的获取的数据是从小到大的
*/
static inline void *minHeapPop(minHeap *s)
{
void *e;
minHeapEntry mhEntry;
if (s->currHeapCnt)
{
e = *s->data;
minHeapShiftDown(s, 1u, s->data[--s->currHeapCnt]);
mhEntry.minHeapIndex = MIN_HEAP_INVALID_IDX;
s->elemSetEntry(e, &mhEntry);
return e;
}
return NULL;
}
/*
* 删除一个节点,我们使用最后一个节点替换需要删除的这个节点
* 2、如果最后这个节点比删除节点的父节点小,我们需要执行UP操作
* 3、如果是根节点或者最后这个节点比删除节点的父节点大,我们需要执行DOWN操作
*/
static inline INT32 minHeapRemove(minHeap *s, INT32 minHeapIndex, void *e)
{
INT32 parent;
void *last;
minHeapEntry mhEntry;
if (minHeapIndex != MIN_HEAP_INVALID_IDX)
{
last = s->data[--s->currHeapCnt];
parent = (minHeapIndex - 1) / 2;
/* 使用last替换e的位置 */
s->data[minHeapIndex] = last;
if (minHeapIndex > 0
&& s->elemCompare(s->data[parent], last))
{
minHeapShiftUp(s, minHeapIndex, last);
}
else
{
minHeapShiftDown(s, minHeapIndex, last);
}
mhEntry.minHeapIndex = MIN_HEAP_INVALID_IDX;
s->elemSetEntry(e, &mhEntry);
s->data[s->currHeapCnt] = NULL;
return MIN_HEAP_OK;
}
return MIN_HEAP_ERROR;
}
/*
* 初始化构建小根堆
* s -- 小根堆管理节点
* rlimit -- 小根堆资源管理,当资源不足时,每次扩展为rlimit的3倍
* elemCompare -- 小根堆宿主节点比较函数
* elemSetEntry -- 设置嵌入在小根堆宿主节点里面的小根堆entry值
*/
static inline INT32 minHeapCtor
(
CHAR *descName,
minHeap *s,
INT32 rlimit,
INT32 (*elemCompare)(void *, void *),
void (*elemSetEntry)(void *, minHeapEntry *)
)
{
void **pData;
if (descName == NULL
|| s == NULL
|| elemCompare == NULL
|| elemCompare == NULL)
{
return MIN_HEAP_ERROR;
}
pData = MALLOC(MODE_COMM, rlimit * sizeof(*pData));
if (pData == NULL)
{
return MIN_HEAP_ERROR;
}
s->data = pData;
s->elemCompare = elemCompare;
s->elemSetEntry = elemSetEntry;
s->currHeapCnt = 1;
s->heapRlimit = rlimit;
strncpy(s->name, descName, MIN_HEAP_NAME_LEN);
return MIN_HEAP_OK;
}
static inline void minHeapDtor(minHeap *s)
{
if (s->data)
{
FREE(MODE_COMM, s->data);
}
return;
}
===========================================================================
分割线,下面使用小根堆的定时器实例:
struct timerStruct
{
struct timeval timerout;
struct timeval expire;
minHeapEntry mhEntry;
}timer;
void getCurrTime(struct timeval *tp)
{
struct timeval ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
tp->tv_sec = ts.tv_sev;
tp->tv_usec = ts.tv_nsec / 1000;
return;
}
void timerAdd(struct timeval *now, struct timeval *tv, struct timeval *expire)
{
expire->tv_sec = now->tv_sec + tv->tv_sec ;
expire->tv_usec = now->tv_usec + tv->tv_usec;
if (expire->tv_usec >= 1000000)
{
expire->tv_sec++;
expire->tv_usec -= 1000000;
}
return;
}
/* 1 --> a > b */
static inline INT32 TimerAfter(struct timeval *a, struct timeval *b)
{
long ases, bses;
if (a->tv_sec == b->tv_sec)
{
return ((a->tv_usec - b->tv_usec) > 0);
}
ases = (long)(a->tv_sec);
bses = (long)(b->tv_sec);
return ((ases - bses) > 0);
}
/* 1 --> a >= b */
static inline INT32 TimerAfterEq(struct timeval *a, struct timeval *b)
{
long ases, bses;
if (a->tv_sec == b->tv_sec)
{
return ((a->tv_usec - b->tv_usec) >= 0);
}
ases = (long)(a->tv_sec);
bses = (long)(b->tv_sec);
return ((ases - bses) >= 0);
}
/* 1 --> a < b */
static inline INT32 TimerBefore(struct timeval *a, struct timeval *b)
{
return TimerAfter(b, a);
}
/* 1 --> a <= b */
static inline INT32 TimerBeforeEq(struct timeval *a, struct timeval *b)
{
return TimerAfterEq(b, a);
}
/* 构建小根堆关键字大小比较,用于调整小根堆,elem1 > elem2 返回1,否则返回0 */
static INT32 minHeapCompare(void *elem1, void *elem2)
{
struct timerStruct *ev1 = (struct timerStruct *)elem1;
struct timerStruct *ev2 = (struct timerStruct *)elem2;
if (TimerAfter(&ev1->evExpire, &ev2->evExpire))
{
return 1;
}
return 0;
}
static void minHeapSet(void *elem, minHeapEntry *entry)
{
struct timerStruct *ev = (struct timerStruct *)elem;
ev->mhEntry.minHeapIndex = entry->minHeapIndex;
return;
}
初始化 && 运行定时器:
minHeap timeHeap;
struct timerStruct ev1 ;
struct timerStruct ev2 ;
struct timeval now1, tv1;
struct timeval now2, tv2;
minHeapCtor("timer", &timeHeap, 4000, minHeapCompare, minHeapSet);
getCurrTime(&now1);
tv1.tv_sec = 1;
timerAdd(&now1, &tv1, &ev1.expire);
getCurrTime(&now2);
tv2.tv_sec = 2;
timerAdd(&now2, &tv2, &ev2.expire);
minHeapInsert(&timeHeap, &ev1);
minHeapInsert(&timeHeap, &ev2);
while (1)
{
计算需要睡眠的时间
1、如果没有定时器,那么永远睡眠
2、如果有定时器,计算还需要睡眠多久(小跟堆第一个节点减去当前时间),如果已经超期,则不睡眠
epoll() 睡眠或者立马返回
处理定时器
1、获取当前时间now
struct timerStruct ev;
while ((ev = minHeapTop(&timeHeap)))
{
if (TimerAfter(&ev->expire, now ))
{
最小的都没超期直接返回
}
//处理超期
minHeapRemove();先删除
在调用定时器的回调函数
}
}