博客
关于我
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连接mysql
查看>>
NodeJs连接Oracle数据库
查看>>
nodejs配置express服务器,运行自动打开浏览器
查看>>
NodeMCU教程 http请求获取Json中文乱码解决方案
查看>>
Nodemon 深入解析与使用
查看>>
NodeSession:高效且灵活的Node.js会话管理工具
查看>>
node~ http缓存
查看>>
node不是内部命令时配置node环境变量
查看>>
node中fs模块之文件操作
查看>>
Node中同步与异步的方式读取文件
查看>>
node中的get请求和post请求的不同操作【node学习第五篇】
查看>>
Node中的Http模块和Url模块的使用
查看>>
Node中自启动工具supervisor的使用
查看>>
Node入门之创建第一个HelloNode
查看>>
node全局对象 文件系统
查看>>
Node出错导致运行崩溃的解决方案
查看>>
Node响应中文时解决乱码问题
查看>>
node基础(二)_模块以及处理乱码问题
查看>>
node安装卸载linux,Linux运维知识之linux 卸载安装node npm
查看>>
node安装及配置之windows版
查看>>