本文共 3347 字,大约阅读时间需要 11 分钟。
本文将介绍并实现一个跨平台、支持多线程的简易定时器对象,粒度为毫秒级别。该定时器对象通过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展示了定时器的使用场景:
#includestatic 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/