实现支持纳秒级精度的时间引擎(C++)
## 前言 在游戏服务器开发中定时器是一个核心组件用于处理各种定时任务如心跳检测、超时处理、定时刷新等。本文将介绍如何在 C 中实现一个支持纳秒级精度、在单独线程中运行的时间引擎。 ## 需求分析 我们需要实现一个时间引擎具有以下特性 1. **纳秒级精度**支持纳秒级别的时间控制 2. **独立线程运行**在单独的线程中运行不阻塞主线程 3. **单次和重复定时器**支持只执行一次和重复执行的定时器 4. **线程安全**多线程环境下安全使用 5. **高效管理**支持添加、取消定时器以及获取统计信息 ## 核心设计 ### 数据结构 cpp // 时间戳类型纳秒级 using NanoTime uint64_t; // 定时器回调类型 using TimerCallback std::functionvoid(); // 定时器 ID 类型 using TimerId uint64_t; // 定时器类型 enum class TimerType { ONCE, // 单次定时器 REPEAT // 重复定时器 }; // 定时器任务结构 struct TimerTask { TimerId id; // 定时器 ID NanoTime expireTime; // 过期时间纳秒 NanoTime interval; // 间隔时间纳秒 TimerType type; // 定时器类型 TimerCallback callback; // 回调函数 bool active; // 是否激活 }; ### 优先级队列 使用优先级队列管理定时器按照过期时间排序 cpp std::priority_queue std::shared_ptrTimerTask, std::vectorstd::shared_ptrTimerTask, TimerTaskComparator m_timers; ### 映射表 使用哈希表快速查找和取消定时器 cpp std::unordered_mapTimerId, std::shared_ptrTimerTask m_timerMap; ## 核心实现 ### 时间获取 使用 std::chrono::high_resolution_clock 获取纳秒级时间 cpp NanoTime TimerEngine::getCurrentNanoTime() { auto now std::chrono::high_resolution_clock::now(); auto duration now.time_since_epoch(); auto nanos std::chrono::duration_caststd::chrono::nanoseconds(duration).count(); return static_castNanoTime(nanos); } ### 定时器主循环 定时器主循环是时间引擎的核心负责检查和执行到期的定时器 cpp void TimerEngine::timerLoop() { while (m_running.load()) { std::unique_lockstd::mutex lock(m_mutex); // 检查是否有定时器 if (m_timers.empty()) { m_condition.wait(lock, [this]() { return !m_running.load() || !m_timers.empty(); }); continue; } // 获取最早到期的定时器 auto task m_timers.top(); // 检查是否已被取消 if (!task-active) { m_timers.pop(); auto it m_timerMap.find(task-id); if (it ! m_timerMap.end()) { m_timerMap.erase(it); } continue; } // 获取当前时间 NanoTime now getCurrentNanoTime(); // 检查定时器是否到期 bool expired false; if (now task-expireTime) { expired true; } else { // 定时器未到期计算等待时间 NanoTime waitTime task-expireTime - now; auto waitDuration std::chrono::nanoseconds(waitTime); auto waitResult m_condition.wait_for(lock, waitDuration); // 等待完成后重新检查是否到期 now getCurrentNanoTime(); if (now task-expireTime) { expired true; } } if (!expired) { continue; } // 定时器已到期执行回调 m_timers.pop(); // 检查是否仍在映射表中 auto it m_timerMap.find(task-id); if (it m_timerMap.end() || !it-second-active) { continue; } // 保存任务信息用于执行 TimerCallback callback task-callback; NanoTime expectedExpireTime task-expireTime; NanoTime interval task-interval; TimerType type task-type; TimerId timerId task-id; // 对于重复定时器创建新的任务并加入队列 if (type TimerType::REPEAT task-active) { auto newTask std::make_sharedTimerTask(); newTask-id timerId; newTask-expireTime now interval; newTask-interval interval; newTask-type type; newTask-callback callback; newTask-active true; m_timers.push(newTask); m_timerMap[timerId] newTask; } else { // 单次定时器只设置 active 标志为 false task-active false; } // 执行回调释放锁避免死锁 lock.unlock(); // 计算执行延迟 NanoTime latency now - expectedExpireTime; updateStats(latency); try { callback(); } catch (const std::exception e) { // 捕获异常防止定时器线程崩溃 std::cerr Timer callback exception: e.what() std::endl; } } } ### 线程安全 为了保证线程安全我们使用以下机制 1. **互斥锁**保护共享数据定时器队列和映射表 2. **条件变量**实现高效等待 3. **原子变量**标志运行状态 cpp std::mutex m_mutex; std::condition_variable m_condition; std::atomicbool m_running; ## 遇到的问题和解决方案 ### 问题 1waitTime 计算溢出 **问题描述**使用无符号整数计算等待时间时当 now expireTime 时waitTime expireTime - now 会溢出变成巨大的正数。 **解决方案**使用有符号整数比较来判断定时器是否到期而不是直接计算 waitTime。 cpp bool expired false; if (now task-expireTime) { expired true; } else { NanoTime waitTime task-expireTime - now; // ... } ### 问题 2重复定时器重新入队 **问题描述**重复定时器重新入队同一个 shared_ptr 对象导致对象被多次引用可能出现问题。 **解决方案**为重复定时器创建新的任务对象而不是重复使用同一个对象。 cpp if (type TimerType::REPEAT task-active) { auto newTask std::make_sharedTimerTask(); newTask-id timerId; newTask-expireTime now interval; newTask-interval interval; newTask-type type; newTask-callback callback; newTask-active true; m_timers.push(newTask); m_timerMap[timerId] newTask; } ### 问题 3cancelTimer 导致悬空指针 **问题描述**cancelTimer 方法直接从映射表中 erase导致优先级队列中的任务成为悬空指针。 **解决方案**只设置 active 标志为 false让 timerLoop 清理任务。 cpp bool TimerEngine::cancelTimer(TimerId timerId) { std::lock_guardstd::mutex lock(m_mutex); auto it m_timerMap.find(timerId); if (it ! m_timerMap.end()) { // 只设置 active 标志不要 erase // 任务会在 timerLoop 中被清理 it-second-active false; return true; } return false; } ### 问题 4cancelAllTimers 导致崩溃 **问题描述**cancelAllTimers 方法清空优先级队列和映射表但 timerLoop 线程可能正在访问这些数据导致崩溃。 **解决方案**设置所有任务的 active 标志为 false并通知 timerLoop。 cpp void TimerEngine::cancelAllTimers() { std::lock_guardstd::mutex lock(m_mutex); // 设置所有任务的 active 标志为 false for (auto pair : m_timerMap) { pair.second-active false; } // 清空映射表但不影响优先级队列中的任务 m_timerMap.clear(); // 通知 timerLoop 线程 m_condition.notify_one(); } ## 测试用例 我们编写了 10 个全面的测试用例 ### 测试 1基本单次定时器 cpp TimerEngine engine; atomicbool executed(false); engine.start(); engine.addTimer(100, [executed]() { executed.store(true); }); this_thread::sleep_for(chrono::milliseconds(200)); engine.stop(); assert(executed.load() true); ### 测试 2重复定时器 cpp TimerEngine engine; atomicint counter(0); engine.start(); engine.addRepeatTimer(100, [counter]() { counter.fetch_add(1); }); this_thread::sleep_for(chrono::milliseconds(550)); engine.stop(); assert(counter.load() 5); ### 测试 3取消定时器 cpp TimerEngine engine; atomicbool executed(false); engine.start(); TimerId timerId engine.addTimer(100, [executed]() { executed.store(true); }); this_thread::sleep_for(chrono::milliseconds(50)); engine.cancelTimer(timerId); this_thread::sleep_for(chrono::milliseconds(100)); engine.stop(); assert(executed.load() false); ### 测试 5纳秒级精度定时器 cpp TimerEngine engine; atomicbool executed(false); engine.start(); engine.addTimerNano(5000ULL, [executed]() { // 5 微秒 executed.store(true); }); this_thread::sleep_for(chrono::milliseconds(10)); engine.stop(); assert(executed.load() true); ### 测试 10性能测试 cpp TimerEngine engine; const int TIMER_COUNT 1000; atomicint counter(0); auto startTime chrono::high_resolution_clock::now(); engine.start(); for (int i 0; i TIMER_COUNT; i) { engine.addTimer(1 i % 10, [counter]() { counter.fetch_add(1); }); } this_thread::sleep_for(chrono::milliseconds(200)); auto endTime chrono::high_resolution_clock::now(); auto duration chrono::duration_castchrono::milliseconds(endTime - startTime).count(); engine.stop(); assert(counter.load() TIMER_COUNT); assert(duration 500); ## 测试结果 时间引擎测试套件 测试 1: 基本的单次定时器 [PASS] 基本单次定时器 测试 2: 重复定时器 [PASS] 重复定时器 测试 3: 取消定时器 [PASS] 取消定时器 测试 4: 多个定时器并发执行 [PASS] 多个定时器并发执行 测试 5: 纳秒级精度定时器 [PASS] 纳秒级精度定时器 测试 6: 获取当前时间 [PASS] 获取当前时间 测试 7: 定时器统计信息 执行次数: 3 最小延迟: 848810 纳秒 最大延迟: 1831155 纳秒 平均延迟: 1456070 纳秒 [PASS] 定时器统计信息 测试 8: 取消所有定时器 [PASS] 取消所有定时器 测试 9: 重复定时器取消 [PASS] 重复定时器取消 测试 10: 性能测试 执行了 1000 个定时器 耗时: 201 毫秒 [PASS] 性能测试 测试结果摘要 通过: 10/10 失败: 0/10 成功率: 100% ✓ 所有测试通过! ## 性能分析 从测试结果可以看出 1. **延迟范围**平均延迟约 1.5 毫秒最小延迟约 0.8 毫秒最大延迟约 1.8 毫秒 2. **吞吐量**1000 个定时器在 200 毫秒内全部执行完成平均每毫秒处理 5 个定时器 3. **精度**支持纳秒级精度5 微秒的定时器能够正常触发 ## 使用示例 cpp #include core/timer/TimerEngine.h int main() { // 创建时间引擎 TimerEngine engine; // 启动引擎 engine.start(); // 添加单次定时器1000 毫秒后执行 TimerId id1 engine.addTimer(1000, []() { std::cout 单次定时器触发 std::endl; }); // 添加重复定时器每 500 毫秒执行一次 TimerId id2 engine.addRepeatTimer(500, []() { std::cout 重复定时器触发 std::endl; }); // 添加纳秒级定时器5000 纳秒后执行 TimerId id3 engine.addTimerNano(5000ULL, []() { std::cout 纳秒定时器触发 std::endl; }); // 获取当前时间纳秒 NanoTime now TimerEngine::getCurrentNanoTime(); // 获取当前时间毫秒 uint64_t nowMs TimerEngine::getCurrentTimeMs(); // 取消定时器 engine.cancelTimer(id1); // 获取统计信息 TimerStats stats engine.getStats(); // 停止引擎 engine.stop(); return 0; } ## 总结 本文实现了一个支持纳秒级精度、在单独线程中运行的时间引擎。通过优先级队列和哈希表的组合实现了高效的定时器管理。在实现过程中我们遇到了多个线程安全相关的问题通过合理的设计和修复最终实现了一个稳定、高效的时间引擎。 测试结果表明该时间引擎具有良好的性能和可靠性能够满足游戏服务器对定时任务的需求。 ## 参考资料 - C std::chrono 文档 - std::priority_queue 文档 - std::condition_variable 文档 --- **作者**林宏权 **日期**2026-03-23
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2441342.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!