博客
关于我
C 实现一个跨平台的定时器 论述
阅读量:440 次
发布时间:2019-03-06

本文共 3347 字,大约阅读时间需要 11 分钟。

跨平台多线程C定时器对象开发与实现

引言

本文将介绍并实现一个跨平台、支持多线程的简易定时器对象,粒度为毫秒级别。该定时器对象通过sctimer.h库实现,适用于需要异步、非阻塞定时任务的高效场景。

接口概览

首先,我们来看定时器的核心接口——st_add

extern int st_add(int start, int cut, int intval, vdel_f timer, void* arg, bool fb);

该函数接收以下参数:

  • start:延迟启动时间,0表示立即启动,单位为毫秒。
  • cut:执行次数,0表示永久循环,1表示单次执行。
  • intval:每次执行的间隔时间,单位为毫秒。
  • timer:定时器执行的函数指针。
  • arg:函数调用的参数指针。
  • fb:启用多线程标志,true表示异步执行,false表示同步执行。

该函数返回定时器的唯一ID。

数据结构设计

定时器的核心数据结构为stnode,表示单个定时器对象:

struct stnode {    int id;          // 定时器的唯一ID    time_t stime;    // 最早执行时间(秒)    int ms;          // 剩余等待时间(毫秒)    int cut;        // 剩余执行次数    int intval;      // 下次执行间隔(毫秒)    int type;       // 定时器类型:0表示同步,1表示异步    vdel_f timer;    // 定时器执行函数    void* arg;      // 函数参数    struct stnode* next; // 下一个定时器对象指针};

定时器管理的核心结构为stlist,用于管理多个定时器对象:

struct stlist {    int lock;         // 加锁标志    int nowid;       // 已使用的最大定时器ID    int status;      // 状态:0表示停止,1表示运行中    pthread_t tid;    // 主循环线程ID    struct stnode* head; // 定时器链表头节点};

业务逻辑设计

添加定时器

st_add函数负责将定时器对象添加到管理链表中,并启动相应的执行线程:

int st_add(int start, int cut, int intval, vdel_f timer, void* arg, bool fb) {    struct stnode* now = _new_stnode(start, cut, intval, timer, arg, fb);    _stlist_add(&_st, now);    return now->id;}

删除定时器

st_del函数负责删除指定ID的定时器对象:

inline void st_del(int st) {    struct stnode* sn = _stlist_del(&_st, st);    if (sn) free(sn);}

定时器执行机制

定时器的核心执行逻辑在_stlist_loop函数中:

static void* _stlist_loop(struct stlist* st) {    while (st->head) {        pthread_testcancel();        int nowt = _sleeptime(st);        if (nowt <= 0 || st->head->cut == 1) {            _slnode_again_run(st);        } else {            SLEEPMS(nowt);        }    }    st->status = 0;    return NULL;}

多线程支持

定时器支持两种模式:同步和异步。异步模式通过线程池实现,以避免主线程阻塞:

static void* _slnode_timer(struct stnode* sn) {    pthread_detach(pthread_self());    sn->timer(sn->arg);    return NULL;}static void _slnode_again_run(struct stlist* st) {    struct stnode* sn = st->head;    st->head = sn->next;    if (sn->cut == 1) {        free(sn);        return;    }    sn->cut = (sn->cut > 0) ? sn->cut - 1 : 0;    int s = sn->intval + sn->ms;    sn->stime += s / 1000;    sn->ms = s % 1000;        if (sn->type) {        pthread_t tid;        pthread_create(&tid, NULL, (void* (*)(void*))_slnode_timer, sn);    } else {        sn->timer(sn->arg);    }    _stlist_add(st, sn);}

跨平台支持

为了实现跨平台支持,特别是Windows和Linux的差异,我们引入了原子锁和时间获取函数:

// 跨平台原子锁#define _INT_USLEEP (2)#define ATOM_LOCK(v) while(ATOM_SET(v, 1)) usleep(_INT_USLEEP)// 时间获取#if defined(_MSC_VER)#include 
#include
#define SLEEPMS(m) Sleep(m)extern int gettimeofday(struct timeval* tv, void* tz);#else#include
#include
#define SLEEPMS(m) usleep(m * 1000)#endif

测试与验证

测试代码test_sctimer.c展示了定时器的使用场景:

#include 
static int _sm = 0;static void _timer(void* arg) { char tstr[64]; sh_times(tstr, 64); printf("%p + %d => %s\n", arg, ++_sm, tstr);}int main(int argc, char* argv) { st_add(0, 5, 2000, _timer, (void*)1, false); st_add(3000, 2, 2000, _timer, (void*)2, false); st_add(4000, 1, 2000, _timer, (void*)3, false); int tid = st_add(0, 0, 1000, _timer, (void*)4, true); SLEEPMS(5000); st_del(tid); st_add(100, 0, 5000, _timer, (void*)5, false); sh_pause(); return 0;}

总结

通过以上设计,我们成功实现了一个跨平台、支持多线程的简易定时器对象。该定时器对象兼顾了异步执行、线程安全和高效性能,适用于需要定时任务调度的复杂应用场景。

转载地址:http://hniyz.baihongyu.com/

你可能感兴趣的文章
nodejs+nginx获取真实ip
查看>>
nodejs-mime类型
查看>>
NodeJs——(11)控制权转移next
查看>>
NodeJS、NPM安装配置步骤(windows版本)
查看>>
NodeJS、NPM安装配置步骤(windows版本)
查看>>
nodejs与javascript中的aes加密
查看>>
nodejs中Express 路由统一设置缓存的小技巧
查看>>
nodejs中express的使用
查看>>
Nodejs中的fs模块的使用
查看>>
NodeJS使用淘宝npm镜像站的各种姿势
查看>>
nodejs包管理工具对比:npm、Yarn、cnpm、npx
查看>>
NodeJs单元测试之 API性能测试
查看>>
nodejs图片转换字节保存
查看>>
nodejs在Liunx上的部署生产方式-PM2
查看>>
nodejs字符与字节之间的转换
查看>>
NodeJs学习笔记001--npm换源
查看>>
NodeJs学习笔记002--npm常用命令详解
查看>>
nodejs学习笔记一——nodejs安装
查看>>
NodeJS实现跨域的方法( 4种 )
查看>>
nodejs封装http请求
查看>>