在C++开发中,计时器是很多场景下需要用到的功能,比如定时执行任务、周期性检测状态、超时控制等。要实现一个可靠的计时器,需要结合高精度时间测量和多线程技术,避免传统时间函数的精度不足和阻塞主线程的问题。

高精度时间测量基础
C++11引入了<chrono>库,提供了跨平台的高精度时间测量能力,相比传统的<time.h>中的time函数,精度可以达到纳秒级别,非常适合计时器场景使用。
常用的chrono组件包括:
- 时钟类:system_clock(系统时钟,可转换为时间戳)、steady_clock(单调时钟,不会被系统时间调整影响,适合计时)、high_resolution_clock(高精度时钟,通常是steady_clock的别名)
- 时间间隔类:duration,用于表示一段时间,支持不同精度的时间单位转换
- 时间点类:time_point,表示某个时刻
下面是获取当前时间并计算时间间隔的示例代码:
#include <iostream>
#include <chrono>
#include <thread>
int main() {
// 获取开始时间点
auto start = std::chrono::steady_clock::now();
// 模拟耗时操作
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 获取结束时间点
auto end = std::chrono::steady_clock::now();
// 计算时间间隔,转换为毫秒
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "耗时: " << duration.count() << " 毫秒" << std::endl;
return 0;
}
单线程简单计时器实现
如果不需要同时处理其他逻辑,可以使用单线程+sleep的方式实现简单的计时器,但是这种方式会阻塞当前线程,不适合复杂场景。
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
// 简单计时器函数,interval为间隔毫秒数,callback为回调函数,times为执行次数,0表示无限执行
void simple_timer(int interval, std::function<void()> callback, int times = 0) {
int count = 0;
while (times == 0 || count < times) {
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
callback();
count++;
}
}
// 测试回调函数
void test_callback() {
auto now = std::chrono::system_clock::now();
auto time_t_now = std::chrono::system_clock::to_time_t(now);
std::cout << "定时任务执行,当前时间: " << std::ctime(&time_t_now) << std::endl;
}
int main() {
// 每1000毫秒执行一次,总共执行3次
simple_timer(1000, test_callback, 3);
return 0;
}
多线程可控制计时器实现
实际开发中需要计时器在后台运行,不阻塞主线程,同时支持启动、停止、重启等操作,这时候就需要结合多线程来实现。
实现思路:
- 创建一个工作线程,在线程中循环检查是否到达执行时间
- 使用条件变量+互斥锁控制线程的等待和唤醒,避免忙等消耗CPU
- 提供启动、停止、设置间隔等接口,保证线程安全
完整实现代码如下:
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <atomic>
class Timer {
public:
Timer() : is_running_(false), interval_ms_(0) {}
~Timer() {
stop();
}
// 启动计时器,interval为间隔毫秒数,callback为回调函数
void start(int interval, std::function<void()> callback) {
// 如果已经在运行,先停止
if (is_running_.load()) {
stop();
}
interval_ms_ = interval;
callback_ = callback;
is_running_.store(true);
// 启动工作线程
worker_thread_ = std::thread(&Timer::worker_func, this);
}
// 停止计时器
void stop() {
if (is_running_.load()) {
is_running_.store(false);
// 唤醒等待的条件变量
cv_.notify_one();
if (worker_thread_.joinable()) {
worker_thread_.join();
}
}
}
// 重启计时器,使用原来的间隔和回调
void restart() {
if (interval_ms_ > 0 && callback_) {
start(interval_ms_, callback_);
}
}
private:
// 工作线程函数
void worker_func() {
auto next_time = std::chrono::steady_clock::now();
while (is_running_.load()) {
next_time += std::chrono::milliseconds(interval_ms_);
// 等待到下一个执行时间,或者被停止信号唤醒
std::unique_lock<std::mutex> lock(mutex_);
if (cv_.wait_until(lock, next_time, [this]() { return !is_running_.load(); })) {
// 被唤醒且计时器已停止,退出循环
break;
}
// 执行回调
if (is_running_.load() && callback_) {
callback_();
}
}
}
private:
std::atomic<bool> is_running_; // 计时器是否运行中
int interval_ms_; // 时间间隔,毫秒
std::function<void()> callback_; // 回调函数
std::thread worker_thread_; // 工作线程
std::mutex mutex_; // 互斥锁
std::condition_variable cv_; // 条件变量
};
// 测试回调函数
void timer_callback() {
auto now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
std::cout << "计时器触发,时间戳(毫秒): " << millis << std::endl;
}
int main() {
Timer timer;
// 启动计时器,每500毫秒执行一次
timer.start(500, timer_callback);
std::cout << "计时器已启动,运行3秒" << std::endl;
// 主线程等待3秒
std::this_thread::sleep_for(std::chrono::seconds(3));
// 停止计时器
timer.stop();
std::cout << "计时器已停止" << std::endl;
// 重启计时器,再运行2秒
timer.restart();
std::cout << "计时器已重启,运行2秒" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
// 再次停止
timer.stop();
std::cout << "程序结束" << std::endl;
return 0;
}
实现注意事项
- 优先使用
steady_clock进行计时,避免系统时间调整导致计时器执行异常 - 多线程操作共享变量时,使用
std::atomic或者互斥锁保证线程安全 - 工作线程中使用条件变量的
wait_until方法,既可以精准等待到指定时间,又可以在需要停止时及时唤醒,避免sleep无法中断的问题 - 计时器析构时要确保工作线程已经退出,避免野引用问题
- 如果回调函数执行时间较长,可能会影响下一次定时任务的触发时间,需要根据实际需求调整实现逻辑
适用场景扩展
上述实现的是周期性执行的计时器,如果需要实现单次延迟执行的计时器,只需要修改工作线程逻辑,执行一次回调后就退出即可。另外也可以扩展支持动态调整时间间隔、注册多个回调函数等功能,满足更复杂的业务需求。