服务器架构设计2------线程的周期执行

说到服务器,通常可以想象都是一个while(1)无限循环,当某种条件触发之后,则跳出循环。

然而,有些时候,则需要服务器针对某些event,周期性的执行特定操作,如何实现呢。

一个简单的方法就是,建立event的node list,节点信息包括(timer,event私有变量,回调函数,执行周期interval),主线程每个周期首先访问node list,如果时间条件满足,执行回调函数。

如果有网络服务和读写磁盘的需求,又会用到epoll和libaio。同时会为epoll和libaio其堵塞函数(epoll_wait, io_getevents)设定超时时间(根据线程执行周期来设定),来完成周期性执行线程函数的功能。其大体结构如下所示:



定时器,回调函数的实现大体如下:

inline uint64_t get_current_clock(void)
{
    struct timespec tp;
    clock_gettime(CLOCK_MONOTONIC, &tp);
    return (uint64_t)tp.tv_sec * 1e3 + (uint64_t)(tp.tv_nsec / 1e6);
}

static void heap_insert_helper(timer_event_t* heap[], uint32_t idx)
{
    if(idx <= 0) {
        return;
    }
    uint32_t p = (idx - 1) >> 1;
    if(heap[p]->time <= heap[idx]->time) {
        return;
    } else {
        timer_event_t* tmp = heap[p];
        heap[p] = heap[idx];
        heap[idx] = tmp;
        heap_insert_helper(heap, p);
    }
}

static void event_heap_insert(timer_ctx_t* ctx, timer_event_t* evt)
{
    timer_event_t** heap = ctx->heap;
    uint32_t idx = ctx->nevts++;
    heap[idx] = evt;
    heap_insert_helper(heap, idx);
}

static void heap_del_helper(timer_event_t* heap[], uint32_t nevts, uint32_t idx)
{
    if(idx >= nevts) {
        return;
    }

    uint32_t lchild = idx * 2 + 1;
    uint32_t rchild = idx * 2 + 2;
    uint32_t i = idx;

    if(lchild < nevts && (heap[i]->time > heap[lchild]->time)) {
        i = lchild;
    }

    if(rchild < nevts && (heap[i]->time > heap[rchild]->time)) {
        i = rchild;
    }
    
    if(i != idx) {
        timer_event_t* tmp = heap[i];
        heap[i] = heap[idx];
        heap[idx] = tmp;
        heap_del_helper(heap, nevts, i);
    }
}

static timer_event_t* event_heap_pop(timer_ctx_t* ctx)
{
    timer_event_t* evt = ctx->heap[0];
    ctx->heap[0] = ctx->heap[--ctx->nevts];
    heap_del_helper(ctx->heap, ctx->nevts, 0);
    return evt;
}

static void
event_re_enqueue(timer_ctx_t* ctx, timer_event_t* evt)
{
    if(evt->enable && evt->interval > 0) {
        evt->time += evt->interval;
        if(evt->count > 0) {
            evt->count--;
            if(evt->count == 0) {
                evt->interval = 0;
            }
        }
        event_heap_insert(ctx, evt);
    } else {
        mpool_put(evt);
    }
}

void timer_run(timer_ctx_t *ctx)
{
    while(ctx->nevts > 0 
          && ctx->heap[0]->time <= get_current_clock()) {
        timer_event_t *evt = event_heap_pop(ctx);
        if(evt->enable && evt->cb) {
            evt->cb(evt->data);
        }
        event_re_enqueue(ctx, evt);
    }
}

timer_ctx_t* timer_init(timer_ctx_t* ctx)
{
    if(!ctx) {
        return NULL;
    }

    memset(ctx, 0, sizeof(timer_ctx_t));

    //make sure mpool is large enough
    ctx->mpool = mpool_calloc(MAX_EVENT_COUNT, sizeof(timer_event_t));
//    ctx->mpool = mpool_init(sizeof(timer_event_t), 
//            (sizeof(timer_event_t) + sizeof(void*)) * MAX_EVENT_COUNT + 512, 
//            NULL);

    if(!ctx->mpool) {
        goto error;
    }

    return ctx;

error:
    mpool_cleanup(ctx->mpool);
    return NULL;
}

void timer_cleanup(timer_ctx_t *ctx) 
{

    if(!ctx) {
        return;
    }
    mpool_cleanup(ctx->mpool);
    ctx->mpool = NULL;
}

int 
timer_register(
    timer_ctx_t *ctx, 
    timer_cb_t cb, 
    void* data, 
    uint32_t delay, 
    uint32_t interval, 
    uint32_t count)
{
    timer_event_t *evt = NULL;
    if(ctx->nevts >= MAX_EVENT_COUNT) {
        goto err;
    }

    evt = (timer_event_t*)mpool_get(ctx->mpool);
    if(!evt) {
        goto err;
    }
    memset(evt, 0, sizeof(timer_event_t));

    int idx = mpool_get_idx(ctx->mpool, evt);
    if(idx < 0) {
        goto err;
    }

    evt->id = idx;
    evt->time = get_current_clock() + delay;
    evt->cb = cb;
    evt->data = data;
    evt->interval = interval;
    evt->count = count;
    evt->enable = 1;

    event_heap_insert(ctx, evt);
    
    return idx;

err:
    if(evt) {
        mpool_put(evt);
    }    
    return -1;
}

void timer_cancel(timer_ctx_t *ctx, int id)
{
    timer_event_t *evt = mpool_get_by_idx(ctx->mpool, id);
    if(evt) {
        evt->enable = 0;
    }
}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值