C++中经典的定时器库与实现方式
在C++中,有多种方式实现定时器功能,而不是简单地使用while(1)循环轮询。每种方法各有优劣,适用于不同的场景。下面详细介绍几种经典的定时器实现方式。
1. C++标准库定时器方法
1.1. std::chrono + std::thread
这是最基础的方式,使用新线程sleep后再执行回调函数。
cpp
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
class Timer {
public:
template<typename Function>
void setTimeout(Function function, int delay) {
std::thread t([=]() {
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
function();
});
t.detach();
}
template<typename Function>
void setInterval(Function function, int interval) {
std::thread t([=]() {
while(true) {
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
function();
}
});
t.detach();
}
};
// 使用示例
int main() {
Timer timer;
// 3秒后执行
timer.setTimeout([]() {
std::cout << "Timeout executed!" << std::endl;
}, 3000);
// 每1秒执行一次
timer.setInterval([]() {
static int count = 0;
std::cout << "Interval executed! Count: " << ++count << std::endl;
if(count >= 5) std::exit(0); // 5次后退出
}, 1000);
// 保持主线程运行
std::cin.get();
return 0;
}
触发机制:创建新线程,在指定时间sleep后执行回调函数。优点是简单易懂,缺点是每个定时器需要一个线程,不适合大量定时器。
1.2. std::condition_variable + std::mutex
使用条件变量可以实现更精确的时间控制与线程同步:
cpp
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <queue>
#include <map>
class AdvancedTimer {
private:
std::mutex mutex;
std::condition_variable cv;
bool stop = false;
std::thread worker;
uint64_t nextId = 0;
struct TimerTask {
uint64_t id;
std::chrono::steady_clock::time_point expiry;
std::chrono::milliseconds interval;
std::function<void()> callback;
bool repeat;
bool operator<(const TimerTask& other) const {
return expiry > other.expiry; // 小顶堆
}
};
std::priority_queue<TimerTask> tasks;
std::map<uint64_t, bool> cancelled; // 标记已取消的定时器
void run() {
while(true) {
std::unique_lock<std::mutex> lock(mutex);
if(tasks.empty()) {
cv.wait(lock, [this] { return stop || !tasks.empty(); });
}
if(stop && tasks.empty()) break;
if(!tasks.empty()) {
auto now = std::chrono::steady_clock::now();
auto& task = tasks.top();
if(task.expiry <= now) {
// 检查是否被取消
if(!cancelled.count(task.id) || !cancelled[task.id]) {
// 非重复定时器
if(!task.repeat) {
task.callback();
}
// 重复定时器
else {
// 先移除再添加,避免死锁
TimerTask t = task;
tasks.pop();
lock.unlock();
t.callback();
lock.lock();
// 重新计算下一次触发时间
t.expiry = now + t.interval;
tasks.push(t);
}
}
tasks.pop();
cancelled.erase(task.id);
} else {
// 等待到下个任务的到期时间或被唤醒
cv.wait_until(lock, task.expiry);
}
}
}
}
public:
AdvancedTimer() {
worker = std::thread(&AdvancedTimer::run, this);
}
~AdvancedTimer() {
{
std::lock_guard<std::mutex> lock(mutex);
stop = true;
}
cv.notify_all();
if(worker.joinable()) worker.join();
}
uint64_t setTimeout(std::function<void()> callback, std::chrono::milliseconds delay) {
std::lock_guard<std::mutex> lock(mutex);
uint64_t id = nextId++;
TimerTask task{id, std::chrono::steady_clock::now() + delay, delay, callback, false};
tasks.push(task);
cv.notify_one();
return id;
}
uint64_t setInterval(std::function<void()> callback, std::chrono::milliseconds interval) {
std::lock_guard<std::mutex> lock(mutex);
uint64_t id = nextId++;
TimerTask task{id, std::chrono::steady_clock::now() + interval, interval, callback, true};
tasks.push(task);
cv.notify_one();
return id;
}
void cancel(uint64_t id) {
std::lock_guard<std::mutex> lock(mutex);
cancelled[id] = true;
}
};
// 使用示例
int main() {
AdvancedTimer timer;
auto id1 = timer.setTimeout([]() {
std::cout << "One-time timer triggered!" << std::endl;
}, std::chrono::milliseconds(3000));
auto id2 = timer.setInterval([]() {
static int count = 0;
std::cout << "Periodic timer triggered! Count: " << ++count << std::endl;
if(count == 3) {
std::cout << "Stopping after 3 executions" << std::endl;
std::exit(0);
}
}, std::chrono::milliseconds(1000));
// 2秒后取消第一个定时器
timer.setTimeout([id1, &timer]() {
std::cout << "Cancelling one-time timer" << std::endl;
timer.cancel(id1);
}, std::chrono::milliseconds(2000));
std::cin.get();
return 0;
}
触发机制:使用优先队列管理多个定时任务,条件变量等待最接近的定时器到期。适合需要管理大量定时器的场景,精度较高,资源利用合理。
2. POSIX系统定时器 (Linux/Unix)
2.1. timer_create + timer_settime
POSIX提供了高精度的定时器API,通过信号或线程触发回调:
cpp
#include <iostream>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <functional>
#include <map>
class PosixTimer {
private:
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
static std::map<timer_t, std::function<void()>> callbacks;
static void timerHandler(union sigval sv) {
timer_t* timerId = (timer_t*)sv.sival_ptr;
auto it = callbacks.find(*timerId);
if(it != callbacks.end()) {
it->second();
}
}
public:
PosixTimer() {
sev.sigev_notify = SIGEV_THREAD; // 使用新线程执行回调
sev.sigev_notify_function = timerHandler;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create");
}
}
~PosixTimer() {
timer_delete(timerid);
callbacks.erase(timerid);
}
void setTimeout(std::function<void()> callback, int milliseconds) {
callbacks[timerid] = callback;
// 一次性定时器
its.it_value.tv_sec = milliseconds / 1000;
its.it_value.tv_nsec = (milliseconds % 1000) * 1000000;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
}
}
void setInterval(std::function<void()> callback, int milliseconds) {
callbacks[timerid] = callback;
// 重复定时器
its.it_value.tv_sec = milliseconds / 1000;
its.it_value.tv_nsec = (milliseconds % 1000) * 1000000;
its.it_interval.tv_sec = milliseconds / 1000;
its.it_interval.tv_nsec = (milliseconds % 1000) * 1000000;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
}
}
};
std::map<timer_t, std::function<void()>> PosixTimer::callbacks;
// 使用示例
int main() {
PosixTimer timer;
// 3秒后执行
timer.setTimeout([]() {
std::cout << "Timer expired after 3 seconds!" << std::endl;
}, 3000);
// 保持程序运行
while(true) {
pause();
}
return 0;
}
触发机制:由操作系统内核管理定时器,当定时器超时时,会触发一个信号或创建一个新线程执行回调函数。精度高,适用于实时系统,但需要处理信号安全问题。
3. Windows系统定时器API
3.1. CreateWaitableTimer
Windows提供了高精度的等待定时器:
cpp
#include <windows.h>
#include <iostream>
#include <functional>
#include <thread>
class WinTimer {
private:
HANDLE timer;
std::function<void()> callback;
bool repeat;
int intervalMs;
static DWORD WINAPI TimerThreadProc(LPVOID lpParam) {
WinTimer* self = (WinTimer*)lpParam;
LARGE_INTEGER dueTime;
dueTime.QuadPart = -(self->intervalMs * 10000LL); // 100纳秒单位
if (!SetWaitableTimer(self->timer, &dueTime, self->repeat ? self->intervalMs : 0, NULL, NULL, FALSE)) {
std::cerr << "Failed to set timer" << std::endl;
return 1;
}
WaitForSingleObject(self->timer, INFINITE);
if (self->callback) {
self->callback();
}
return 0;
}
public:
WinTimer() {
timer = CreateWaitableTimer(NULL, TRUE, NULL);
}
~WinTimer() {
if (timer != NULL) {
CloseHandle(timer);
}
}
void setTimeout(std::function<void()> cb, int milliseconds) {
callback = cb;
repeat = false;
intervalMs = milliseconds;
std::thread thread(TimerThreadProc, this);
thread.detach();
}
void setInterval(std::function<void()> cb, int milliseconds) {
callback = cb;
repeat = true;
intervalMs = milliseconds;
std::thread thread(TimerThreadProc, this);
thread.detach();
}
};
// 使用示例
int main() {
WinTimer timer;
// 3秒后执行
timer.setTimeout([]() {
std::cout << "Timer triggered after 3 seconds!" << std::endl;
}, 3000);
// 保持程序运行
Sleep(5000);
return 0;
}
触发机制:创建一个内核对象,当定时器超时时,该对象变成signaled状态,等待该对象的线程会被唤醒。适合Windows平台,精度高,可以与其他内核对象一起等待。
4. 跨平台高级库
4.1. Boost.Asio
Boost.Asio提供了跨平台的异步I/O模型,包括高性能定时器:
cpp
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind/bind.hpp>
class BoostTimer {
private:
boost::asio::io_context io;
boost::asio::deadline_timer timer;
public:
BoostTimer() : timer(io) {}
template<typename Function>
void setTimeout(Function function, int milliseconds) {
timer.expires_from_now(boost::posix_time::milliseconds(milliseconds));
timer.async_wait([function](const boost::system::error_code& error) {
if (!error) {
function();
}
});
io.run();
}
template<typename Function>
void setInterval(Function function, int milliseconds) {
auto recursiveWait = [this, function, milliseconds](const boost::system::error_code& error) {
if (!error) {
function();
timer.expires_from_now(boost::posix_time::milliseconds(milliseconds));
timer.async_wait(recursiveWait);
}
};
timer.expires_from_now(boost::posix_time::milliseconds(milliseconds));
timer.async_wait(recursiveWait);
io.run();
}
};
// 使用示例
int main() {
BoostTimer timer;
// 3秒后执行
timer.setTimeout([]() {
std::cout << "Timer expired after 3 seconds!" << std::endl;
}, 3000);
return 0;
}
触发机制:使用反应器(Reactor)模式,底层使用epoll(Linux)、kqueue(BSD)、IOCP(Windows)等高性能I/O多路复用技术,当定时器超时时,事件循环会调用相应的回调函数。适合构建高性能网络服务器和应用。
4.2. Qt QTimer
Qt框架为GUI和非GUI应用提供QTimer类:
cpp
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
class MyObject : public QObject {
Q_OBJECT
public:
MyObject(QObject* parent = nullptr) : QObject(parent) {
// 创建单次定时器
QTimer::singleShot(3000, this, &MyObject::handleSingleShot);
// 创建周期性定时器
periodicTimer = new QTimer(this);
connect(periodicTimer, &QTimer::timeout, this, &MyObject::handlePeriodicTimer);
periodicTimer->start(1000); // 每1000毫秒触发一次
}
private slots:
void handleSingleShot() {
qDebug() << "Single shot timer triggered!";
}
void handlePeriodicTimer() {
static int count = 0;
qDebug() << "Periodic timer triggered!" << ++count;
if(count >= 5) {
periodicTimer->stop();
qDebug() << "Stopped periodic timer after 5 executions";
QCoreApplication::quit(); // 退出应用
}
}
private:
QTimer* periodicTimer;
};
int main(int argc, char* argv[]) {
QCoreApplication app(argc, argv);
MyObject obj;
return app.exec();
}
#include "main.moc" // 需要这个来处理Qt的元对象编译
触发机制:Qt使用事件循环(QEventLoop)处理所有事件,包括定时器事件。当定时器超时时,Qt事件系统会分发一个定时器事件到对象的事件处理器,然后调用关联的槽函数。适合Qt应用程序,与Qt事件系统无缝集成。
5. 事件循环与回调机制
现代定时器通常采用事件驱动架构,而不是轮询。主要原理是:
- 注册定时器和回调函数
- 系统在后台监控时间
- 当定时器超时时,系统调用注册的回调函数
- 应用程序在事件循环中处理这些回调
5.1. 简单事件循环定时器实现
cpp
#include <iostream>
#include <vector>
#include <chrono>
#include <functional>
#include <algorithm>
#include <thread>
#include <mutex>
#include <condition_variable>
class EventLoopTimer {
private:
struct TimerInfo {
size_t id;
std::chrono::steady_clock::time_point expiry;
std::chrono::milliseconds interval;
std::function<void()> callback;
bool repeat;
};
std::vector<TimerInfo> timers;
std::mutex mutex;
std::condition_variable cv;
bool running = true;
size_t nextId = 0;
std::thread loopThread;
void eventLoop() {
while(running) {
auto now = std::chrono::steady_clock::now();
std::vector<size_t> expiredIds;
std::vector<TimerInfo> expiredTimers;
{
std::lock_guard<std::mutex> lock(mutex);
// 找出所有已超时的定时器
for(auto it = timers.begin(); it != timers.end();) {
if(now >= it->expiry) {
expiredTimers.push_back(*it);
expiredIds.push_back(it->id);
it = timers.erase(it);
} else {
++it;
}
}
}
// 执行回调,不在锁内执行以避免死锁
for(auto& timer : expiredTimers) {
timer.callback();
// 重新添加重复定时器
if(timer.repeat) {
timer.expiry = now + timer.interval;
addTimer(timer);
}
}
// 计算到下一个定时器的时间
std::chrono::milliseconds sleepTime(10); // 默认休眠10ms
{
std::lock_guard<std::mutex> lock(mutex);
if(!timers.empty()) {
auto nextExpiry = std::min_element(timers.begin(), timers.end(),
[](const TimerInfo& a, const TimerInfo& b) {
return a.expiry < b.expiry;
})->expiry;
auto timeUntilNext = std::chrono::duration_cast<std::chrono::milliseconds>(
nextExpiry - std::chrono::steady_clock::now());
if(timeUntilNext.count() > 0) {
sleepTime = timeUntilNext;
} else {
sleepTime = std::chrono::milliseconds(1); // 至少休眠1ms
}
}
}
// 休眠或等待唤醒
std::unique_lock<std::mutex> lock(mutex);
cv.wait_for(lock, sleepTime);
}
}
size_t addTimer(TimerInfo timer) {
std::lock_guard<std::mutex> lock(mutex);
timer.id = nextId++;
timers.push_back(timer);
cv.notify_one();
return timer.id;
}
public:
EventLoopTimer() {
loopThread = std::thread(&EventLoopTimer::eventLoop, this);
}
~EventLoopTimer() {
{
std::lock_guard<std::mutex> lock(mutex);
running = false;
}
cv.notify_one();
if(loopThread.joinable()) loopThread.join();
}
size_t setTimeout(std::function<void()> callback, int milliseconds) {
TimerInfo timer;
timer.expiry = std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
timer.interval = std::chrono::milliseconds(milliseconds);
timer.callback = callback;
timer.repeat = false;
return addTimer(timer);
}
size_t setInterval(std::function<void()> callback, int milliseconds) {
TimerInfo timer;
timer.expiry = std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
timer.interval = std::chrono::milliseconds(milliseconds);
timer.callback = callback;
timer.repeat = true;
return addTimer(timer);
}
void cancel(size_t id) {
std::lock_guard<std::mutex> lock(mutex);
timers.erase(std::remove_if(timers.begin(), timers.end(),
[id](const TimerInfo& timer) { return timer.id == id; }),
timers.end());
}
};
// 使用示例
int main() {
EventLoopTimer timer;
// 3秒后执行
timer.setTimeout([]() {
std::cout << "Single timer triggered!" << std::endl;
}, 3000);
// 每1秒执行一次
size_t intervalId = timer.setInterval([]() {
static int count = 0;
std::cout << "Periodic timer triggered! Count: " << ++count << std::endl;
if(count >= 5) std::exit(0);
}, 1000);
// 2.5秒后取消
timer.setTimeout([intervalId, &timer]() {
std::cout << "Cancelling periodic timer..." << std::endl;
timer.cancel(intervalId);
}, 2500);
std::cin.get();
return 0;
}
触发机制:使用优先队列或排序列表管理所有定时器,计算到下一个定时器的时间,然后休眠。当有新的定时器加入或需要取消时,会唤醒线程重新计算。这种方式CPU占用低,适合大量定时器管理。
总结与对比
| 定时器类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| std::thread + sleep | 简单易懂,标准库支持 | 每个定时器需要一个线程,精度低 | 简单应用,少量定时器 |
| std::condition_variable | 精度较高,资源利用合理 | 实现复杂,需要线程同步知识 | 需要精确控制的多定时器场景 |
| POSIX timers | 精度高,系统级支持 | 仅限Unix/Linux,信号处理复杂 | 服务器应用,实时系统 |
| Windows timers | 高精度,与其他内核对象集成 | 仅限Windows平台 | Windows桌面/服务器应用 |
| Boost.Asio | 跨平台,高性能,支持大量并发 | 需要Boost依赖,学习曲线陡峭 | 高性能网络服务器,跨平台应用 |
| Qt QTimer | 与GUI事件循环集成,使用简单 | 需要Qt框架,重量级 | Qt应用程序,GUI定时任务 |
| 事件循环定时器 | 资源占用少,适合大量定时器 | 实现复杂 | 高性能服务器,游戏引擎 |
每种方式的触发机制各有不同:
- 轮询方式:主动检查时间,简单但CPU占用高
- 休眠唤醒:设置休眠时间,到期后唤醒执行
- 信号/事件驱动:系统在后台监控,超时时发送信号/事件
- 回调机制:注册回调函数,超时时由系统调用
选择定时器实现时,应考虑平台兼容性、精度要求、定时器数量、资源占用以及与现有架构的集成度。对于现代C++应用,推荐使用Boost.Asio或标准库的条件变量结合事件循环的方式实现高性能定时器。