5.0 KiB
5.0 KiB
Plan: libtask 定时器优化 — timerfd 替代 SIGEV_THREAD
日期: 2026-06-15
目标: 消除定时器每次超时创建/销毁内核线程的开销(~1000线程/秒)
问题根因
myTask.c 使用 timer_create(CLOCK_REALTIME, SIGEV_THREAD, ...) 模式,每次超时内核创建新线程执行回调。9 个 app × 3 个定时器(10/100/1000ms) = 27 个定时器,每秒约 1000 次线程创建/销毁。回调仅做 task_event_send(几微秒),线程创建开销远大于实际工作。
方案概要
用 Linux timerfd + epoll + 单例管理器线程 替代 SIGEV_THREAD:
- 每个定时器用
timerfd_create创建文件描述符 - 一个持久化 manager 线程用
epoll_wait监听所有 timerfd - timerfd 可读时在 manager 线程中调用原始回调
- API 签名全部不变,调用方零修改
涉及文件
| 文件 | 改动 |
|---|---|
src/public/libtask/src/myTask.c |
主要修改 — 重写定时器子系统 |
release/inc/myTask.h |
无改动 — stru_task_timer_t 是不透明 void* |
结构体变更
// myTask.c 内部 (对外不可见)
typedef struct
{
char name[64];
int timerfd; // 替代 timer_t timerid
timer_func_cb fun;
void *arg;
uint32_t timeout_ms;
int flags;
int active;
int running; // 1=回调执行中
int in_epoll; // 1=timerfd 已注册到 epoll
pthread_mutex_t mutex;
pthread_cond_t cond; // 新增: stop/destroy 等回调完成
int ref_count;
} stru_task_timer;
变更: timer_t timerid → int timerfd,新增 pthread_cond_t cond、int in_epoll
管理器线程设计
全局状态:
static int g_epfd; // epoll 实例
static int g_eventfd; // wakeup eventfd
static pthread_t g_mgr_thread;
static pthread_mutex_t g_mgr_mutex; // 串行化调用者 epoll_ctl
static int g_mgr_init_done;
timer_manager_thread():
g_epfd = epoll_create1(0)
g_eventfd = eventfd(0, EFD_NONBLOCK)
epoll_ctl(ADD, g_eventfd) // wakeup fd 永久在 epoll 中
while(1):
epoll_wait(events, -1)
for each ready fd:
if fd == g_eventfd: read() 清除; continue
read(fd, &expirations) // 清除过期计数
lock(p_timer->mutex)
if active:
running = 1; unlock
fun(arg) // 调用原始回调
lock
running = 0
cond_broadcast(&cond)
if !PERIODIC: active = 0
unlock
关键同步设计
1. 动态 epoll 增删
- 添加 (
task_timer_start): 调用者持有g_mgr_mutex做epoll_ctl(ADD),然后 write eventfd 唤醒 manager - 删除 (
task_timer_stop): 调用者持有g_mgr_mutex做epoll_ctl(DEL) - Manager 线程永不做
epoll_ctl,避免跨线程 epoll 操作竞态 g_mgr_mutex仅防护调用者之间并发的 epoll_ctl
2. stop/destroy 同步(替代 usleep 自旋)
用 running + condvar 替代当前 while(running) usleep(1000) 忙等:
stop 路径:
lock(mutex)
active = 0
timerfd_settime(its={0,0}) // disarm
while(running):
cond_wait(&cond, &mutex) // 阻塞等待,零 CPU
if in_epoll: epoll_ctl(DEL)
unlock
manager 路径 (回调结束后):
lock(mutex)
running = 0
cond_broadcast(&cond) // 唤醒 stop 等待者
if !PERIODIC: active = 0
unlock
锁顺序注意: stop 中 epoll_ctl(DEL) 之前先 unlock(&p->mutex) 再 lock(&g_mgr_mutex),避免与 start 路径的 lock(&p->mutex) → lock(&g_mgr_mutex) 形成 ABBA 死锁。
3. 惰性初始化
首次 task_timer_start 时调用 timer_manager_ensure_init(),用 g_mgr_init_done + g_mgr_mutex 做双重检查锁定,确保 manager 线程仅创建一次。
API 实现概要
task_timer_create
- 参数校验 → calloc 结构体 → 初始化字段
timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK)- 初始化 mutex + cond → 返回指针(未启动)
task_timer_start
timer_manager_ensure_init()惰性初始化 manager- lock →
timerfd_settime设间隔 → 若!in_epoll则epoll_ctl(ADD)+ write eventfd →active=1→ unlock
task_timer_stop
- lock →
timerfd_settime({0,0})disarms →active=0 while(running) cond_wait等回调完成- 若
in_epoll: unlock → lock g_mgr →epoll_ctl(DEL)→ unlock g_mgr → lock →in_epoll=0→ unlock
task_timer_destroy
- 调用
task_timer_stop(含同步等待 + epoll 移除) - lock →
ref_count--;若 >0 直接返回 close(timerfd)→ 销毁 mutex/cond →free
task_timer_restart
stop() → 更新 timeout_ms → start()
task_timer_is_active
lock → 读 active → unlock → 返回(不变)
验证
./release/build.sh编译零错误零警告- 确认
myTask.h未改动(API 透明兼容) - 确认 app_sys.cpp 等所有调用方无需修改(9 个 app 线程的定时器用法不变)
./test/RTU启动运行,验证各 app 的 run_cnt 正常递增