2. 定时器事件驱动周期任务调度器
任务说明
本案例实现一个软件定时器模块,支持单次(one-shot)与周期(periodic)两种模式,并集成到事件循环中统一调度。定时器到期后产生事件,由事件循环分发至对应回调处理。
软件定时器设计
定时器模块维护一个按到期时间排序的链表,每个节点包含到期时刻、周期标志、回调函数及用户参数。事件循环每次迭代时检查链表头部节点是否到期,若到期则触发回调;周期定时器到期后重新计算下次到期时间并保持链表有序,单次定时器则从链表中移除。该设计将定时器检查与事件分发解耦,使定时器事件与其它事件(如IO事件)共用同一调度循环。
代码实现
// timer.h
#ifndef TIMER_H
#define TIMER_H
#include <stdint.h>
#include <stdbool.h>
typedef void (*timer_cb_t)(void *arg);
typedef struct timer_node {
uint32_t expire_ms; // 到期时间
uint32_t period_ms; // 周期值,0表示单次
timer_cb_t cb;
void *arg;
struct timer_node *next;
} timer_node_t;
void timer_init(void);
uint32_t tick_ms(void);
void timer_start(timer_node_t *node, uint32_t delay_ms,
uint32_t period_ms, timer_cb_t cb, void *arg);
void timer_stop(timer_node_t *node);
void timer_process(void);
#endif
// timer.c
#include "timer.h"
#include <stdlib.h>
static timer_node_t *head = NULL;
static volatile uint32_t tick_counter = 0;
void timer_init(void) {
head = NULL;
tick_counter = 0;
}
uint32_t tick_ms(void) {
return tick_counter;
}
// 由硬件定时器中断或主循环周期性调用
void timer_tick_update(void) {
tick_counter++;
}
static void list_insert(timer_node_t *node) {
timer_node_t **pp = &head;
while (*pp && (*pp)->expire_ms <= node->expire_ms) {
pp = &(*pp)->next;
}
node->next = *pp;
*pp = node;
}
void timer_start(timer_node_t *node, uint32_t delay_ms,
uint32_t period_ms, timer_cb_t cb, void *arg) {
node->expire_ms = tick_ms() + delay_ms;
node->period_ms = period_ms;
node->cb = cb;
node->arg = arg;
node->next = NULL;
list_insert(node);
}
void timer_stop(timer_node_t *node) {
timer_node_t **pp = &head;
while (*pp) {
if (*pp == node) {
*pp = node->next;
node->next = NULL;
return;
}
pp = &(*pp)->next;
}
}
void timer_process(void) {
uint32_t now = tick_ms();
while (head && head->expire_ms <= now) {
timer_node_t *node = head;
head = node->next;
node->next = NULL;
if (node->period_ms > 0) {
// 周期定时器:重新计算到期时间并插入链表
node->expire_ms = now + node->period_ms;
list_insert(node);
}
if (node->cb) {
node->cb(node->arg);
}
}
}
// main.c 示例
#include "timer.h"
#include <stdio.h>
static timer_node_t t1, t2;
void one_shot_cb(void *arg) {
printf("[%u] 单次定时器触发, arg=%d\n", tick_ms(), *(int*)arg);
}
void periodic_cb(void *arg) {
printf("[%u] 周期定时器触发\n", tick_ms());
}
int main(void) {
int val = 42;
timer_init();
// 单次定时:100ms后触发一次
timer_start(&t1, 100, 0, one_shot_cb, &val);
// 周期定时:每50ms触发一次
timer_start(&t2, 30, 50, periodic_cb, NULL);
// 模拟事件循环:每次循环推进1ms
for (int i = 0; i < 200; i++) {
timer_tick_update(); // 模拟1ms时基
timer_process(); // 检查并处理到期定时器
}
return 0;
}
编译与运行
gcc -Wall -o timer_demo main.c timer.c
./timer_demo
验证方法
运行程序后观察输出:单次定时器应在tick约100时触发一次,打印arg=42;周期定时器首次在tick约30时触发,之后每50ms触发一次(即tick约80、130、180处)。若输出符合上述时间规律且单次定时器仅触发一次,则判定实现正确。可通过调整delay_ms与period_ms参数验证不同定时组合。