在构建网络服务器、游戏后端及分布式系统时,“时间”成为不可忽视的核心维度。本章从超时处理、心跳检测等现实场景出发,深入剖析定时器的本质及其关键衡量指标。
在网络服务器、游戏后端、分布式系统中,"时间"是一个核心维度。以下场景揭示了其重要性:

| 场景 | 问题描述 | 没有定时器会怎样? |
|---|---|---|
| TCP连接超时 | 客户端建立连接后长时间不发送数据 | 连接永久占用资源,服务器崩溃 |
| 心跳检测 | 检测客户端是否存活 | 无法感知客户端宕机,资源泄漏 |
| 延迟任务 | 30分钟后发送验证码 | 需要轮询数据库,效率极低 |
| 周期性调度 | 每秒统计QPS并上报 | 需要独立线程+sleep,精度差 |
| 缓存过期 | Redis键的TTL机制 | 内存无限增长 |
| 技能冷却 | 游戏中技能释放后的冷却时间 | 玩家可无限释放技能 |
定时器的核心功能在于赋予程序在预定时间执行特定代码的能力。
定时器最基础的应用是网络编程中的超时设置,每个连接都需要为此配置超时时间。
┌─────────────────────────────────────────────────────────────┐
│ TCP连接超时处理流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 客户端连接 ──► 创建连接对象 ──► 添加超时定时器(30s) │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ 等待数据到达 │ │
│ └──────┬───────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ ▼ ▼ ▼ │
│ [数据到达] [30s超时] [连接关闭] │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 删除定时器 关闭连接 删除定时器 │
│ 处理数据 记录日志 │
│ │
└─────────────────────────────────────────────────────────────┘
代码示例:连接超时管理
#include
#include
#include class ConnectionManager {
public:
using ConnectionId = uint64_t;
using TimerId = uint64_t;
using TimePoint = std::chrono::steady_clock::time_point;
// 添加连接,设置30秒超时
void AddConnection(ConnectionId conn_id, int fd) {
// 存储连接信息
connections_[conn_id] = {fd, /*last_active=*/Now()};
// 添加超时定时器
TimerId timer_id = timer_manager_.AddTimer(
std::chrono::seconds(30),
[this, conn_id]() {
OnConnectionTimeout(conn_id);
}
);
// 记录定时器ID,方便后续取消
connection_timers_[conn_id] = timer_id;
}
// 连接有活动,重置超时时间(心跳续期)
void RefreshConnection(ConnectionId conn_id) {
auto it = connections_.find(conn_id);
if (it != connections_.end()) {
it->second.last_active = Now();
// 删除旧定时器,添加新定时器
timer_manager_.CancelTimer(connection_timers_[conn_id]);
TimerId new_timer = timer_manager_.AddTimer(
std::chrono::seconds(30),
[this, conn_id]() { OnConnectionTimeout(conn_id); }
);
connection_timers_[conn_id] = new_timer;
}
}
// 超时回调
void OnConnectionTimeout(ConnectionId conn_id) {
printf("Connection %lu timeout, closing...n", conn_id);
CloseConnection(conn_id);
}
private:
TimePoint Now() { return std::chrono::steady_clock::now(); }
struct Connection {
int fd;
TimePoint last_active;
};
std::unordered_map connections_;
std::unordered_map connection_timers_;
TimerManager timer_manager_; // 假设已实现
};
在长连接场景(如WebSocket、RPC框架、游戏服务器)中,心跳是检测对端存活的核心机制。
┌────────────────────────────────────────────────────────────────┐
│ 心跳检测机制 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 服务端 客户端 │
│ │ │ │
│ │◄─────── 心跳请求 ──────────────────────│ 每5秒 │
│ │ │ │
│ │─────── 心跳响应 ──────────────────────►│ │
│ │ │ │
│ │ [更新最后活跃时间] │ │
│ │ │ │
│ │◄─────── 心跳请求 ──────────────────────│ │
│ │─────── 心跳响应 ──────────────────────►│ │
│ │ │ │
│ │ [超过15秒无心跳] │ │
│ │ │ │
│ │ 判定客户端离线 │ │
│ │ 主动关闭连接 │ │
│ │ 释放相关资源 │ │
│ │
└────────────────────────────────────────────────────────────────┘
心跳检测的关键参数:
| 参数 | 典型值 | 说明 |
|---|---|---|
| 心跳间隔 | 5-10秒 | 客户端发送心跳的频率 |
| 超时阈值 | 15-30秒 | 服务端判定离线的时间 |
| 心跳超时次数 | 2-3次 | 连续未响应次数才判定离线 |
延迟任务是定时器的另一项重要应用,常见于订单超时自动取消、验证码过期、定时推送消息及分布式任务调度等场景。
┌─────────────────────────────────────────────────────────────┐
│ 延迟任务执行流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 用户下单 ──► 创建订单 ──► 添加延迟任务(30分钟后执行) │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ 等待30分钟 │ │
│ └──────┬───────┘ │
│ │ │
│ ┌────────────────┴────────────────┐ │
│ ▼ ▼ │
│ [订单已支付] [订单未支付] │
│ │ │ │
│ ▼ ▼ │
│ 取消延迟任务 执行取消订单 │
│ 订单完成 释放库存 │
│ 通知用户 │
│ │
└─────────────────────────────────────────────────────────────┘
代码示例:订单超时处理
#include
#include
#include class OrderService {
public:
using OrderId = std::string;
// 创建订单,设置30分钟超时
void CreateOrder(const OrderId& order_id, int amount) {
// 1. 创建订单记录
orders_[order_id] = {amount, OrderStatus::PENDING, Now()};
// 2. 添加延迟任务:30分钟后检查订单状态
timer_manager_.AddTimer(
std::chrono::minutes(30),
[this, order_id]() {
CheckOrderTimeout(order_id);
}
);
printf("Order %s created, will timeout in 30 minutesn",
order_id.c_str());
}
// 支付订单
void PayOrder(const OrderId& order_id) {
auto it = orders_.find(order_id);
if (it != orders_.end() && it->second.status == OrderStatus::PENDING) {
it->second.status = OrderStatus::PAID;
printf("Order %s paidn", order_id.c_str());
// 注意:定时器仍然会触发,但检查时会发现已支付
}
}
private:
void CheckOrderTimeout(const OrderId& order_id) {
auto it = orders_.this, order_id]() {
CheckOrderTimeout(order_id);
}
);
printf("Order %s created, will timeout in 30 minutesn",
order_id.c_str());
}
// 支付订单
void PayOrder(const OrderId& order_id) {
auto it = orders_.find(order_id);
if (it != orders_.end() && it->second.status == OrderStatus::PENDING) {
it->second.status = OrderStatus::PAID;
printf("Order %s paidn", order_id.c_str());
// 注意:定时器仍然会触发,但检查时会发现已支付
}
}
private:
void CheckOrderTimeout(const OrderId& order_id) {
auto it = orders_.find(order_id);
if (it != orders_.Cod
if (it != orders_.end() && it->second.status == OrderStatus::PENDING) {
// 订单未支付,执行超时逻辑
it->second.status = OrderStatus::TIMEOUT;
printf("Order %s timeout, auto cancelledn", order_id.c_str());
// 释放库存、通知用户等...
ReleaseInventory(it->second.amount);
}
}
void ReleaseInventory(int amount) {
// 释放库存逻辑...
}
enum class OrderStatus { PENDING, PAID, TIMEOUT };
struct Order {
int amount;
OrderStatus status;
std::chrono::steady_clock::time_point create_time;
};
std::unordered_map orders_;
TimerManager timer_manager_;
};
周期性任务需要反复执行,包括监控指标采集、数据库连接池健康检查、日志文件轮转和缓存预热等。
┌─────────────────────────────────────────────────────────────┐
│ 周期性任务执行示意 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 时间轴 ──────────────────────────────────────────────► │
│ │
│ 任务A(每1秒): ●───●───●───●───●───●───●───●───► │
│ │ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │
│ 采集 采集 采集 采集 采集 采集 采集 采集 │
│ │
│ 任务B(每5秒): ●─────────●─────────●─────────► │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 检查 检查 检查 │
│ │
│ 任务C(每10秒): ●───────────────●───────────────► │
│ │ │ │
│ ▼ ▼ │
│ 清理 清理 │
│ │
└─────────────────────────────────────────────────────────────┘
代码示例:周期性任务管理
class MetricsCollector {
public:
MetricsCollector() : qps_(0), latency_sum_(0), request_count_(0) {}
void Start() {
// 每秒采集一次QPS
timer_manager_.AddPeriodicTimer(
std::chrono::seconds(1),
[this]() { CollectAndReport(); }
);
// 每10秒检查一次健康状态
timer_manager_.AddPeriodicTimer(
std::chrono::seconds(10),
[this]() { HealthCheck(); }
);
printf("Metrics collector startedn");
}
void RecordRequest(int latency_ms) {
qps_++;
latency_sum_ += latency_ms;
request_count_++;
}
private:
void CollectAndReport() {
// 计算并上报QPS和平均延迟
int current_qps = qps_.exchange(0); // 原子操作并重置
double avg_latency = 0.0;
if (request_count_ > 0) {
avg_latency = static_cast<double>(latency_sum_) / request_count_;
latency_sum_ = 0;
request_count_ = 0;
}
printf("[Metrics] QPS: %d, AvgLatency: %.2fmsn",
current_qps, avg_latency);
// 上报到监控系统...
}
void HealthCheck() {
printf("[Health] System running normallyn");
// 检查数据库连接、内存使用等...
}
std::atomic<int> qps_;
std::atomic<long long> latency_sum_;
std::atomic<int> request_count_;
TimerManager timer_manager_;
};
通过分析上述场景,可以抽象出定时器的核心接口。无论底层实现是最小堆、时间轮还是红黑树,对外暴露的接口都是一致的。
/**
* 定时器管理器抽象接口
*
* 设计原则:
* 1. 添加定时器返回唯一ID,用于后续取消
* 2. 支持单次定时器和周期定时器
* 3. Tick()由外部事件循环驱动,不创建独立线程
*/
class TimerManager {
public:
using TimerId = uint64_t;
using Duration = std::chrono::milliseconds;
using Callback = std::function<void()>;
virtual ~TimerManager() = default;
/**
* 添加一个单次定时器
* @param delay 延迟时间
* @param callback 到期回调
* @return 定时器ID,用于取消
*/
virtual TimerId AddTimer(Duration delay, Callback callback) = 0;
/**
* 添加一个周期定时器
* @param interval 周期间隔
* @param callback 每次到期回调
* @return 定时器ID,用于取消
*/
virtual TimerId AddPeriodicTimer(Duration interval, Callback callback) = 0;
/**
* 取消定时器
* @param id 要取消的定时器ID
* @return 是否成功取消(定时器可能已执行)
*/
virtual bool CancelTimer(TimerId id) = 0;
/**
* 驱动定时器前进
* @param now 当前时间点
* @return 下一个最近定时器的到期时间(用于设置epoll_wait超时)
*/
virtual std::optional Tick(std::chrono::steady_clock::time_point now) = 0;
/**
* 获取最近定时器的到期时间
* @return 如果有待执行定时器,返回到期时间;否则返回空
*/
virtual std::optional
GetNextExpiry() const = 0;
};
AddTimer 是定时器模块最频繁的操作,其性能直接影响系统吞吐量。
操作流程:
┌─────────────────────────────────────────────────────────────┐
│ AddTimer 执行流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入: delay=5000ms, callback=OnTimeout │
│ │
│ Step 1: 计算到期时间 │
│ expiry = now + delay = 14:30:05.000 │
│ │
│ Step 2: 分配定时器ID │
│ timer_id = atomic_fetch_add(&next_id_, 1) = 42 │
│ │
│ Step 3: 创建定时器对象 │
│ Timer { id: 42, expiry: 14:30:05, cb: OnTimeout } │
│ │
│ Step 4: 插入数据结构 │
│ ┌─────────────────────────────────────┐ │
│ │ 最小堆: O(log n) 上滤操作 │ │
│ │ 时间轮: O(1) 计算槽位插入 │ │
│ │ 红黑树: O(log n) 按key插入 │ │
│ └─────────────────────────────────────┘ │
│ │
│ Step 5: 返回定时器ID │
│ return 42 │
│ │
└─────────────────────────────────────────────────────────────┘
不同实现的AddTimer复杂度:
| 数据结构 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 最小堆 | O(log n) | O(n) | 需要上滤操作 |
| 时间轮 | O(1) | O(n + m) | m为槽数量,直接计算槽位 |
| 红黑树 | O(log n) | O(n) | 按到期时间作为key |
| 跳表 | O(log n) | O(n) | 期望复杂度 |
Tick 是定时器的核心驱动机制,由外部事件循环(如 epoll_wait)负责调用。
操作流程:
┌─────────────────────────────────────────────────────────────┐
│ Tick 执行流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入: now = 14:30:05.100 │
│ │
│ Step 1: 检查是否有到期定时器 │
│ while (top.expiry <= now) │
│ │
│ Step 2: 取出到期定时器 │
│ timer = heap.pop() // 或从时间轮当前槽取出 │
│ │
│ Step 3: 检查是否已取消(惰性删除) │
│ if (canceled_[timer.id]) continue; │
│ │
│ Step 4: 执行回调 │
│ timer.callback() │
│ ├── 可能触发新定时器添加 │
│ ├── 可能取消其他定时器 │
│ └── 可能阻塞(危险!) │
│ │
│ Step 5: 如果是周期定时器,重新添加 │
│ if (timer.is_periodic) { │
│ timer.expiry += timer.interval; │
│ heap.push(timer); │
│ } │
│ │
│ Step 6: 返回下一个到期时间 │
│ return heap.top().expiry - now; │
│ │
└─────────────────────────────────────────────────────────────┘
Tick与事件循环的集成:
#include
#include class EventLoop {
public:
EventLoop() {
epoll_fd_ = epoll_create1(0);
}
void Run() {
while (running_) {
// 计算epoll_wait的超时时间
int timeout_ms = -1; // 默认阻塞
auto next_expiry = timer_manager_.GetNextExpiry();
if (next_expiry.has_value()) {
auto now = std::chrono::steady_clock::now();
auto wait_duration =
std::chrono::duration_cast(
*next_expiry - now
);
timeout_ms = std::max(0, static_cast<int>(wait_duration.count()));
}
// 等待IO事件或定时器到期
struct epoll_event events[128];
int n = epoll_wait(epoll_fd_, events, 128, timeout_ms);
// 处理IO事件
for (int i = 0; i < n; ++i) {
HandleEvent(events[i]);
}
// 驱动定时器
timer_manager_.Tick(std::chrono::steady_clock::now());
}
}
private:
int epoll_fd_;
bool running_ = true;
TimerManager timer_manager_;
};
┌─────────────────────────────────────────────────────────────────────┐
│ AddTimer 与 Tick 的协作 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 业务线程 │ │ IO线程 │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ │ AddTimer(5s, cb1) │ │
│ │─────────────────────────────────────────────► │
│ │ │ │
│ │ ┌────────────────────┐ │ │
│ │ │ TimerHeap: │ │ │
│ │ │ [14:30:05, cb1] │ │ │
│ │ │ [14:30:10, cb2] │ │ │
│ │ └────────────────────┘ │ │
│ │ │ │
│ │ │ epoll_wait│
│ │ │ timeout=5s│
│ │ │ │
│ │ │ [5秒后] │
│ │ │ │
│ │ │ Tick() │
│ │ │ cb1() │
│ │ │ │
│ │ AddTimer(3s, cb3) │ │
│ │─────────────────────────────────────────────► │
│ │ │ │
│ │ ┌────────────────────┐ │ │
│ │ │ TimerHeap: │ │ │
│ │ │ [14:30:08, cb3] │ │ │
│ │ │ [14:30:10, cb2] │ │ │
│ │ └────────────────────┘ │ │
│ │ │ │
│ │
└─────────────────────────────────────────────────────────────────────┘
在设计或选择定时器实现时,需要从以下四个维度进行评估。
定义:定时器实际执行时间与期望执行时间的偏差。
影响因素:
时钟源精度
clock_gettime(CLOCK_MONOTONIC) 可达纳秒级std::chrono::steady_clock 通常封装了上述系统调用gettimeofday() 已废弃,不推荐使用驱动粒度
epoll_wait 的超时参数是毫秒级tick_interval 决定了最小精度系统调度延迟
精度测试代码:
#include
#include
#include void TestTimerPrecision() {
const int test_count = 1000;
const int target_delay_ms = 10;
std::vector<double> errors;
errors.reserve(test_count);
for (int i = 0; i < test_count; ++i) {
auto start = std::chrono::steady_clock::now();
// 模拟定时器等待
std::this_thread::sleep_for(std::chrono::milliseconds(target_delay_ms));
auto end = std::chrono::steady_clock::now();
auto actual_ms = std::chrono::duration_cast(
end - start
).count() / 1000.0;
errors.push_back(actual_ms - target_delay_ms);
}
// 统计分析
double sum = 0, max_err = 0, min_err = 1e9;
for (double e : errors) {
sum += e;
max_err = std::max(max_err, e);
min_err = std::min(min_err, e);
}
double avg_err = sum / test_count;
printf("Timer Precision Test (target: %dms, samples: %d)n",
target_delay_ms, test_count);
printf(" Average error: %.3f msn", avg_err);
printf(" Max error: %.3f msn", max_err);
printf(" Min error: %.3f msn", min_err);
printf(" Jitter range: %.3f msn", max_err - min_err);
}
典型输出:
Timer Precision Test (target: 10ms, samples: 1000)
Average error: 0.042 ms
Max error: 1.234 ms
Min error: -0.012 ms
Jitter range: 1.246 ms
定义:单位时间内能处理的定时器增删操作次数。
测试场景:
┌─────────────────────────────────────────────────────────────┐
│ 吞吐量测试场景 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 场景A:高频添加/取消 │
│ ───────────────────── │
│ for (int i = 0; i < 1000000; ++i) { │
│ id = AddTimer(random_delay, cb); │
│ CancelTimer(id); │
│ } │
│ │
│ 场景B:大量定时器到期 │
│ ───────────────────── │
│ for (int i = 0; i < 100000; ++i) { │
│ AddTimer(1ms, cb); // 同时添加大量1ms后到期的定时器 │
│ } │
│ Tick(); // 触发执行 │
│ │
│ 场景C:周期定时器持续运行 │
│ ───────────────────── │
│ for (int i = 0; i < 10000; ++i) { │
│ AddPeriodicTimer(10ms, cb); │
│ } │
│ for (int i = 0; i < 10000; ++i) { │
│ Tick(); // 持续驱动10秒 │
│ } │
│ │
└─────────────────────────────────────────────────────────────┘
吞吐量测试代码:
#include
#include void TestTimerThroughput(TimerManager& timer_manager) {
const int add_cancel_count = 1000000;
const int expire_count = 100000;
// 测试1:添加+取消吞吐量
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < add_cancel_count; ++i) {
auto id = timer_manager.AddTimer(
std::chrono::milliseconds(1000),
[](){}
);
timer_manager.CancelTimer(id);
}
auto end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast(
end - start
).count();
double ops_per_sec = (add_cancel_count * 2.0) / (ms / 1000.0);
printf("Add/Cancel Throughput: %.0f ops/secn", ops_per_sec);
}
// 测试2:批量到期吞吐量
{
auto start = std::chrono::steady_clock::now();
// 添加大量即将到期的定时器
for (int i = 0; i < expire_count; ++i) {
timer_manager.AddTimer(
std::chrono::milliseconds(1),
[](){}
);
}
// 驱动执行
auto now = std::chrono::steady_clock::now() +
std::chrono::milliseconds(2);
timer_manager.Tick(now);
auto end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast(
end - start
).count();
double expiries_per_sec = expire_count / (ms / 1000.0);
printf("Expiry Throughput: %.0f timers/secn", expiries_per_sec);
}
}
不同实现的吞吐量对比:
| 实现 | Add/Cancel (ops/sec) | Expiry (timers/sec) | 内存/定时器 |
|---|---|---|---|
| 最小堆 | ~200万 | ~50万 | 48 bytes |
| 时间轮 | ~500万 | ~100万 | 56 bytes |
| 红黑树 | ~150万 | ~40万 | 64 bytes |
定义:单个定时器对象占用的内存大小。
内存布局分析:
// 典型Timer结构体
struct Timer {
TimerId id; // 8 bytes
TimePoint expiry; // 8 bytes (64-bit timestamp)
Duration interval; // 8 bytes (周期定时器用)
Callback callback; // 32-48 bytes (std::function)
bool is_periodic; // 1 byte + padding
// 额外字段...
// 总计: ~64-80 bytes
};// 时间轮额外开销
struct TimingWheel {
std::vector> slots; // 槽数组
size_t current_slot; // 当前指针
size_t slot_count; // 槽数量
Duration tick_interval; // 精度
// 每个槽的链表头节点: 16-24 bytes
// 总开销: slot_count * 24 bytes
};
内存优化策略:
使用函数指针代替 std::function
// 原始:std::function 开销大
using Callback = std::function<void()>;// 优化:函数指针 + void* context
using Callback = void(*)(void* ctx);
void* callback_context;
内存池分配
class TimerPool {
public:
Timer* Allocate() {
if (free_list_.empty()) {
return new Timer;
}
Timer* t = free_list_.back();
free_list_.pop_back();
return t;
}
void Deallocate(Timer* t) {
free_list_.push_back(t);
}
private:
std::vector free_list_;
};
紧凑存储
// 将周期定时器和单次定时器分开存储
struct PeriodicTimer {
TimerId id;
TimePoint next_expiry;
Duration interval;
Callback callback;
};struct OneShotTimer {
TimerId id;
TimePoint expiry;
Callback callback;
// 省略 interval 字段
};
定义:定时器模块在多线程环境下正确工作的能力。
三种并发模型:
┌─────────────────────────────────────────────────────────────────────┐
│ 定时器并发模型对比 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 模型1:全局锁(简单但低效) │
│ ───────────────────────── │
│ ┌──────────────────────────────────────────────┐ │
│ │ std::mutex lock_; │ │
│ │ │ │
│ │ TimerId AddTimer(...) { │ │
│ │ std::lock_guard g(lock_); │ │
│ │ return DoAddTimer(...); │ │
│ │ } │ │
│ └──────────────────────────────────────────────┘ │
│ 优点:实现简单 │
│ 缺点:高并发下锁竞争严重,性能下降明显 │
│ │
│ 模型2:线程私有定时器(推荐) │
│ ───────────────────────────── │
│ ┌──────────────────────────────────────────────┐ │
│ │ 每个IO线程独立的TimerManager │ │
│ │ │ │
│ │ Thread1: TimerManager1 │ │
│ │ Thread2: TimerManager2 │ │
│ │ Thread3: TimerManager3 │ │
│ └──────────────────────────────────────────────┘ │
│ 优点:无锁竞争,性能最优 │
│ 缺点:定时器不能跨线程操作 │
│ │
│ 模型3:无锁队列转发 │
│ ───────────────────────────── │
│ ┌──────────────────────────────────────────────┐ │
│ │ 业务线程 ──► 无锁队列 ──► 定时器线程 │ │
│ │ │ │
│ │ AddTimer() { │ │
│ │ queue_.push(TimerCmd{ADD, ...}); │ │
│ │ eventfd_notify(); │ │
│ │ } │ │
│ └──────────────────────────────────────────────┘ │
│ 优点:支持跨线程操作,性能较好 │
│ 缺点:实现复杂,需要eventfd配合 │
│ │
└─────────────────────────────────────────────────────────────────────┘
无锁队列实现示例:
#include
#include // 简单的MPSC(多生产者单消费者)无锁队列
template<typename T>
class MPSCQueue {
public:
struct Node {
T data;
std::atomic next{nullptr};
};
MPSCQueue() {
Node* stub = new Node{};
head_.store(stub);
tail_ = stub;
}
// 生产者调用(多线程安全)
void Push(T&& data) {
Node* node = new Node{std::move(data), nullptr};
Node* prev = head_.exchange(node);
prev->next.store(node);
}
// 消费者调用(单线程)
bool Pop(T& out) {
Node* tail = tail_;
Node* next = tail->next.load();
if (next == nullptr) {
return false;
}
out = std::move(next->data);
tail_ = next;
delete tail;
return true;
}
private:
std::atomic head_;
Node* tail_;
};// 定时器命令
struct TimerCommand {
enum Type { ADD, CANCEL } type;
TimerManager::TimerId id;
std::chrono::milliseconds delay;
std::function<void()> callback;
};// 线程安全的定时器管理器
class ThreadSafeTimerManager {
public:
TimerManager::TimerId AddTimer(
std::chrono::milliseconds delay,
std::function<void()> callback
) {
TimerManager::TimerId id = next_id_.fetch_add(1);
command_queue_.Push(TimerCommand{
TimerCommand::ADD,
id,
delay,
std::move(callback)
});
// 通知定时器线程有新命令
NotifyTimerThread();
return id;
}
// 定时器线程调用
void ProcessCommands() {
TimerCommand cmd;
while (command_queue_.Pop(cmd)) {
if (cmd.type == TimerCommand::ADD) {
timer_manager_.AddTimer(cmd.delay, cmd.callback);
} else {
timer_manager_.CancelTimer(cmd.id);
}
}
}
private:
void NotifyTimerThread() {
// 使用eventfd通知...
}
std::atomic next_id_{0};
MPSCQueue command_queue_;
TimerManager timer_manager_; // 非线程安全
};
┌─────────────────────────────────────────────────────────────────────┐
│ 第一章 核心要点 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 定时器的本质 │
│ ────────────── │
│ "在未来的某个时间点,执行某段代码" │
│ 四大场景:超时处理、心跳检测、延迟任务、周期性调度 │
│ │
│ 2. 两大核心操作 │
│ ────────────── │
│ AddTimer:添加定时器,返回ID用于取消 │
│ Tick:驱动定时器前进,执行到期回调 │
│ │
│ 3. 四大关键指标 │
│ ────────────── │
│ ┌────────────┬────────────┬────────────────────┐ │
│ │ 指标 │ 关注点 │ 影响因素 │ │
│ ├────────────┼────────────┼────────────────────┤ │
│ │ 精度 │ 时间偏差 │ 时钟源、驱动粒度 │ │
│ │ 吞吐量 │ 操作速率 │ 数据结构、算法 │ │
│ │ 内存开销 │ 单定时器 │ 结构设计、内存池 │ │
│ │ 并发安全 │ 多线程 │ 锁策略、无锁设计 │ │
│ └────────────┴────────────┴────────────────────┘ │
│ │
│ 4. 设计选型指南 │
│ ────────────── │
│ • 少量定时器(<1000):最小堆,实现简单 │
│ • 大量定时器(>10000):时间轮,O(1)操作 │
│ • 需要有序遍历:红黑树 │
│ • 高并发场景:线程私有或无锁队列 │
│ │
└─────────────────────────────────────────────────────────────────────┘
场景分析:假设你要实现一个即时通讯服务器,每个用户连接需要设置心跳超时(30秒)和消息发送超时(5秒)。请分析:
性能估算:如果一个定时器操作(AddTimer + CancelTimer)需要1微秒,那么:
设计权衡:为什么大多数网络库(如libevent、Redis)选择最小堆而不是时间轮?请从实现复杂度、内存局部性、精度控制三个角度分析。
综上所述,本章系统地阐述了定时器的核心概念、典型应用场景、关键操作接口与性能评测指标,为后续深入探讨其底层实现奠定了坚实基础。下一章将聚焦于操作系统提供的时间驱动API,剖析