152 lines
5.0 KiB
Markdown
152 lines
5.0 KiB
Markdown
# 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 正常递增
|