事件循环与定时器
本教程介绍 libcc 的事件模型与时间轮计时器,包含示例:创建事件管理器、添加定时器与处理回调。
核心概念
_cc_async_event_t:事件管理器,封装平台轮询(epoll/kqueue/iocp/select),管理事件列表与时间轮。_cc_event_t:具体事件(socket、定时器、文件等),包含 flags、回调与超时时间。- 时间轮:高效的软定时器实现,使用
_cc_add_event_timeout/_cc_kill_event_timeout等 API,支持高并发场景下的定时任务调度。
时间轮机制详解
libcc 使用时间轮算法实现定时器,具有以下特点: - 高效性:O(1) 时间复杂度添加和删除定时器。 - 精度:最小精度为 10ms,受系统调度影响。 - 多级轮:支持多级时间轮,处理不同时间范围的定时器。 - 线程安全:在单线程事件循环中安全,多线程需额外同步。
示例代码(tests/test_event.c)
#include <stdio.h>
#include <libcc.h>
static bool_t timer_cb(_cc_async_event_t *async, _cc_event_t *e, const uint32_t which) {
if (which & _CC_EVENT_TIMEOUT_) {
_cc_logger_info(_T("[event-loop] timeout ident=%d data=%ld"), e->ident, e->data);
/* 停止事件循环 */
async->running = false;
return false;
}
return true;
}
int main(void) {
_cc_async_event_t async;
_cc_event_t *ev;
/* 在平台上注册 poller(macOS 使用 kqueue,Linux 使用 epoll,Windows 使用 IOCP) */
if (!_cc_register_kqueue(&async)) {
fprintf(stderr, "register kqueue failed\n");
return EXIT_FAILURE;
}
/* 添加 2 秒定时器 */
ev = _cc_add_event_timeout(&async, 2000, timer_cb, 42);
if (!ev) {
fprintf(stderr, "failed to add timer\n");
async.free(&async);
return EXIT_FAILURE;
}
/* 事件循环 */
while (async.running) {
async.wait(&async, 500);
}
async.free(&async);
return EXIT_SUCCESS;
}
详细说明
- 注册 Poller:
_cc_register_*函数根据平台选择最优 I/O 多路复用机制。 - 添加定时器:
_cc_add_event_timeout返回事件指针,失败时返回 NULL。 - 事件循环:
async.wait()阻塞等待事件,超时返回继续循环。 - 回调处理:返回
true保持定时器,返回false移除定时器。 - 资源清理:调用
async.free()释放所有资源。
高级用法
周期性定时器
static bool_t periodic_timer(_cc_async_event_t *async, _cc_event_t *e, const uint32_t which) {
if (which & _CC_EVENT_TIMEOUT_) {
_cc_logger_info(_T("Periodic timer triggered"));
// 返回 true 继续定时
return true;
}
return false;
}
// 添加每秒触发的周期性定时器
_cc_add_event_timeout(&async, 1000, periodic_timer, 0);
移除定时器
_cc_event_t *timer = _cc_add_event_timeout(&async, 5000, callback, data);
// 手动移除
_cc_kill_event_timeout(&async, timer);
注意事项
- 回调函数:避免在回调中执行耗时操作,以免阻塞事件循环。
- 定时精度:软定时器精度受系统负载影响,不适用于高精度计时。
- 内存管理:定时器由事件管理器管理,无需手动释放(除非提前移除)。
- 多线程:事件循环应在单线程中运行,多线程共享事件管理器需加锁。
- 平台支持:确保编译时包含对应平台的轮询实现(epoll/kqueue/iocp)。
- 调试:使用
_cc_logger_*记录定时器事件,便于排查问题。 - 资源限制:大量定时器可能消耗内存,监控系统资源使用。