C 语言标准库(如 ANSI C)中并没有一个叫做 timerListener 的原生类或函数。

timerListener 是一个设计模式或编程思想的体现,尤其在事件驱动的编程中非常常见,它的核心思想是:
当一个定时器触发时,程序需要执行一段特定的代码(回调函数),这段代码就是“监听器”(Listener)。
你可以把它想象成设置一个闹钟:
- 设置定时器:你告诉闹钟“10分钟后叫我”。
- 注册监听器:你告诉闹钟“叫我的时候,就播放《好运来》这首歌”。
- 等待:闹钟开始计时。
- 触发:10分钟后,闹钟响了,它执行了你注册的操作——播放《好运来》。
在这个比喻里:

- 定时器:闹钟本身。
- 监听器:播放《好运来》这首歌这个行为。
- 回调函数:实现播放《好运来》的那个函数。
在 C 语言中,我们通过函数指针来实现这种“监听器”模式。
核心实现:函数指针
函数指针是 C 语言实现回调机制的关键,它是一个指向函数的指针,通过这个指针可以间接调用该函数。
定义监听器函数类型
我们定义一个函数指针类型,这个类型代表了所有定时器事件监听器应该具有的“签名”(即参数和返回值)。
// 定义一个函数指针类型 TimerListener // 它接受一个 void* 类型的参数(用于传递自定义数据),返回 void typedef void (*TimerListener)(void* context);
typedef:为一种类型创建一个别名。void (*...):这是一个函数指针。(void* context):这是回调函数的参数。void*是一个通用指针,非常灵活,可以指向任何类型的数据,这使得我们可以向监听器传递额外的信息,比如定时器的ID、用户数据等。void:这是回调函数的返回值,通常事件处理函数不需要返回值。
创建一个简单的定时器结构体
为了管理定时器,我们通常会定义一个结构体来存储它的状态。

typedef struct {
int interval_ms; // 定时间隔(毫秒)
int is_running; // 是否正在运行
TimerListener listener; // 指向监听器函数的指针
void* user_data; // 传递给监听器的用户自定义数据
} Timer;
实现定时器的设置和触发逻辑
我们来实现一个简单的定时器管理器,这个管理器会检查每个定时器是否到期,如果到期,就调用它的监听器。
重要提示:以下代码是一个概念性示例,用于演示 timerListener 的工作原理,它使用了 sleep 来模拟时间流逝,这不是一个高效、精确的多线程定时器实现,在实际应用中,你会使用 select, poll, epoll (Linux) 或 kqueue (BSD/macOS) 等I/O多路复用技术,或者专门的多线程定时器库。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // for sleep()
#include <stdbool.h>
// --- 步骤1: 定义监听器函数类型 ---
typedef void (*TimerListener)(void* context);
// --- 步骤2: 创建定时器结构体 ---
typedef struct {
int interval_ms;
bool is_running;
TimerListener listener;
void* user_data;
} Timer;
// --- 步骤3: 实现一个简单的定时器触发器 ---
// 这个函数会检查所有注册的定时器
void check_and_trigger_timers(Timer* timers, int num_timers) {
for (int i = 0; i < num_timers; i++) {
if (timers[i].is_running) {
// 在真实场景中,这里应该是基于真实时间戳的计算
// 这里我们用 sleep 来模拟
printf("Timer %d is running for %d ms...\n", i, timers[i].interval_ms);
usleep(timers[i].interval_ms * 1000); // usleep 的单位是微秒
printf("Timer %d triggered! Calling listener...\n", i);
// 调用注册的监听器函数,并传递 user_data
if (timers[i].listener) {
timers[i].listener(timers[i].user_data);
}
timers[i].is_running = false; // 触发后停止
}
}
}
// --- 步骤4: 定义具体的监听器函数 ---
// 监听器函数1: 打印一条消息
void on_timer_event1(void* context) {
printf(" -> Listener 1: Hello from Timer! Context: %p\n", context);
}
// 监听器函数2: 打印一个数字
void on_timer_event2(void* context) {
int* num = (int*)context;
printf(" -> Listener 2: The magic number is: %d\n", *num);
}
// 监听器函数3: 什么都不做,仅作演示
void on_timer_event3(void* context) {
(void)context; // 避免未使用参数的警告
printf(" -> Listener 3: This is a silent timer.\n");
}
int main() {
// 创建并初始化几个定时器
Timer timers[3];
// 定时器1
timers[0].interval_ms = 1000; // 1秒
timers[0].is_running = true;
timers[0].listener = on_timer_event1;
timers[0].user_data = NULL; // 不需要额外数据
// 定时器2
int magic_number = 42;
timers[1].interval_ms = 2000; // 2秒
timers[1].is_running = true;
timers[1].listener = on_timer_event2;
timers[1].user_data = &magic_number; // 传递一个整数
// 定时器3
timers[2].interval_ms = 1500; // 1.5秒
timers[2].is_running = true;
timers[2].listener = on_timer_event3;
timers[2].user_data = (void*)0xDEADBEEF; // 传递一个无意义的指针
printf("Starting timers...\n");
// 模拟主循环
for (int i = 0; i < 3; i++) {
check_and_trigger_timers(timers, 3);
// 在真实应用中,这里会是一个阻塞等待,直到有定时器到期或新事件发生
// 而不是像这样循环检查
}
printf("All timers have been processed.\n");
return 0;
}
如何编译和运行
将上述代码保存为 timer_example.c,然后使用 GCC 编译:
gcc timer_example.c -o timer_example ./timer_example
预期输出
由于 usleep 是阻塞的,并且定时器是顺序执行的,输出会像这样:
Starting timers...
Timer 0 is running for 1000 ms...
Timer 0 triggered! Calling listener...
-> Listener 1: Hello from Timer! Context: (nil)
Timer 1 is running for 2000 ms...
Timer 1 triggered! Calling listener...
-> Listener 2: The magic number is: 42
Timer 2 is running for 1500 ms...
Timer 2 triggered! Calling listener...
-> Listener 3: This is a silent timer.
All timers have been processed.
实际应用场景和高级库
上面的例子很简单,但在实际项目中,定时器需求更复杂:
- 高精度:需要纳秒或微秒级的精度。
- 高并发:需要同时管理成百上千个定时器。
- 非阻塞:定时器检查不能阻塞主线程或主事件循环。
- 可取消/重置:能够随时停止或修改一个定时器。
开发者通常会使用成熟的库来处理定时器任务。
在 Linux 环境下:libevent 和 libuv
这些是事件通知库,它们内部实现了高效的多路复用定时器。
libuv 示例:
libuv 是一个跨平台的支持异步 I/O 的库,它的 uv_timer_t 是实现 timerListener 模式的绝佳范例。
#include <stdio.h>
#include <uv.h>
// 这就是我们的 "timerListener" 函数
void timer_callback(uv_timer_t* handle) {
printf("Timer callback triggered!\n");
// 获取用户数据
int* count = (int*)handle->data;
(*count)++;
if (*count >= 5) {
printf("Timer reached 5 counts, stopping.\n");
uv_timer_stop(handle);
}
}
int main() {
uv_loop_t* loop = uv_default_loop();
uv_timer_t timer_req;
int repeat_count = 0;
// 将用户数据与定时器关联
timer_req.data = &repeat_count;
// 初始化定时器
uv_timer_init(loop, &timer_req);
// 启动定时器
// 第一个参数是 loop,第二个是 timer 对象
// 第三个是延迟时间(毫秒),第四个是重复间隔(0表示只运行一次)
uv_timer_start(&timer_req, timer_callback, 1000, 1000);
// 运行事件循环,直到没有更多事件需要处理
uv_run(loop, UV_RUN_DEFAULT);
// 清理
uv_timer_stop(&timer_req);
uv_close((uv_handle_t*)&timer_req, NULL);
return 0;
}
在这个 libuv 的例子中:
uv_timer_t是我们的Timer结构体。timer_callback就是我们的TimerListener函数。uv_timer_start用于“注册”定时器和它的“监听器”。uv_run是主循环,它会非阻塞地等待定时器事件并触发回调。
在嵌入式系统或 RTOS 中
在嵌入式领域,timerListener 模式同样非常普遍,通常由操作系统或硬件抽象层提供定时器服务。
- FreeRTOS:
xTimerCreate()创建一个软件定时器,并为其指定一个回调函数。 - Arduino:
myTimer.attach(callback, interval_ms)是典型的timerListener应用。
| 概念 | 解释 | C 语言实现方式 |
|---|---|---|
timerListener |
一种设计模式,指当定时器触发时被调用的代码块。 | 函数指针 |
| 回调函数 | timerListener 的具体实现,包含了定时器到期后要执行的逻辑。 |
一个符合特定签名的普通 C 函数。 |
| 上下文 | 传递给回调函数的额外数据,用于区分不同定时器或传递状态。 | 使用 void* 指针作为回调函数的参数。 |
| 实际应用 | 在需要延迟执行、周期性任务或超时处理的场景中。 | 使用 libuv, libevent 等高级库,或操作系统提供的定时器API。 |
timerListener 在 C 语言中不是一个具体的“东西”,而是一种通过函数指针实现的强大编程范式,它将“何时执行”(定时器)和“执行什么”(监听器逻辑)分离开来,是构建灵活、模块化事件驱动程序的基础。
