# 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*` | ## 结构体变更 ```c // 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 1. 参数校验 → calloc 结构体 → 初始化字段 2. `timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK)` 3. 初始化 mutex + cond → 返回指针(未启动) ### task_timer_start 1. `timer_manager_ensure_init()` 惰性初始化 manager 2. lock → `timerfd_settime` 设间隔 → 若 `!in_epoll` 则 `epoll_ctl(ADD)` + write eventfd → `active=1` → unlock ### task_timer_stop 1. lock → `timerfd_settime({0,0})` disarms → `active=0` 2. `while(running) cond_wait` 等回调完成 3. 若 `in_epoll`: unlock → lock g_mgr → `epoll_ctl(DEL)` → unlock g_mgr → lock → `in_epoll=0` → unlock ### task_timer_destroy 1. 调用 `task_timer_stop`(含同步等待 + epoll 移除) 2. lock → `ref_count--`;若 >0 直接返回 3. `close(timerfd)` → 销毁 mutex/cond → `free` ### task_timer_restart `stop()` → 更新 `timeout_ms` → `start()` ### task_timer_is_active lock → 读 `active` → unlock → 返回(不变) ## 验证 1. `./release/build.sh` 编译零错误零警告 2. 确认 `myTask.h` 未改动(API 透明兼容) 3. 确认 app_sys.cpp 等所有调用方无需修改(9 个 app 线程的定时器用法不变) 4. `./test/RTU` 启动运行,验证各 app 的 run_cnt 正常递增