跳到主要内容C++中经典的定时器库与实现方式 | 极客日志C++算法
C++中经典的定时器库与实现方式
C++ 中多种经典的定时器实现方式,涵盖标准库(std::chrono+thread, condition_variable)、POSIX 系统定时器、Windows API、Boost.Asio 及 Qt QTimer 等方案。通过对比不同触发机制(轮询、休眠、信号驱动)和优缺点,分析了各自适用场景,如简单应用、实时系统或高性能网络服务,并提供了完整代码示例供参考。
CloudNative6 浏览 C++中经典的定时器库与实现方式
在C++中,有多种方式实现定时器功能,而不是简单地使用while(1)循环轮询。每种方法各有优劣,适用于不同的场景。下面详细介绍几种经典的定时器实现方式。
1. C++标准库定时器方法
1.1. std::chrono + std::thread
这是最基础的方式,使用新线程sleep后再执行回调函数。
#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;
timer.([](){
std::cout << << std::endl;
}, );
timer.([](){
count = ;
std::cout << << ++count << std::endl;
(count >= ) std::();
}, );
std::cin.();
;
}
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
()
setTimeout
"Timeout executed!"
3000
setInterval
static
int
0
"Interval executed! Count: "
if
5
exit
0
1000
get
return
0
触发机制:创建新线程,在指定时间 sleep 后执行回调函数。优点是简单易懂,缺点是每个定时器需要一个线程,不适合大量定时器。
1.2. std::condition_variable + std::mutex
#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));
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,通过信号或线程触发回调:
#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;
timer.setTimeout([](){
std::cout << "Timer expired after 3 seconds!" << std::endl;
}, 3000);
while(true){ pause(); }
return 0;
}
触发机制:由操作系统内核管理定时器,当定时器超时时,会触发一个信号或创建一个新线程执行回调函数。精度高,适用于实时系统,但需要处理信号安全问题。
3. Windows系统定时器API
3.1. CreateWaitableTimer
#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);
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;
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 模型,包括高性能定时器:
#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;
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类:
#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);
}
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 使用事件循环 (QEventLoop) 处理所有事件,包括定时器事件。当定时器超时时,Qt 事件系统会分发一个定时器事件到对象的事件处理器,然后调用关联的槽函数。适合 Qt 应用程序,与 Qt 事件系统无缝集成。
5. 事件循环与回调机制
现代定时器通常采用事件驱动架构,而不是轮询。主要原理是:
- 注册定时器和回调函数
- 系统在后台监控时间
- 当定时器超时时,系统调用注册的回调函数
- 应用程序在事件循环中处理这些回调
5.1. 简单事件循环定时器实现
#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);
{
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);
}
}
}
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;
timer.setTimeout([](){
std::cout << "Single timer triggered!" << std::endl;
}, 3000);
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);
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 或标准库的条件变量结合事件循环的方式实现高性能定时器。