C++中经典的定时器库与实现方式

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. 事件循环与回调机制

现代定时器通常采用事件驱动架构,而不是轮询。主要原理是:

  1. 注册定时器和回调函数
  2. 系统在后台监控时间
  3. 当定时器超时时,系统调用注册的回调函数
  4. 应用程序在事件循环中处理这些回调

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或标准库的条件变量结合事件循环的方式实现高性能定时器。

https://github.com/0voice

相关推荐
BestOrNothing_20152 小时前
C++ 成员函数运算符重载深度解析
c++·八股·运算符重载·operator·this指针·const成员函数·const引用
槿花Hibiscus2 小时前
C++基础:session实现和http server类最终组装
服务器·c++·http·muduo
仰泳的熊猫2 小时前
1116 Come on! Let‘s C
数据结构·c++·算法·pat考试
BTU_YC2 小时前
python 内网部署
开发语言·python
千疑千寻~3 小时前
【QML】C++访问QML控件
c++·qml
yyovoll3 小时前
java线程知识点介绍1
java·开发语言
tyatyatya3 小时前
MATLAB高级可视化与图形处理:绘图美化/交互/导出实战
开发语言·matlab
爱写bug的野原新之助3 小时前
10_异常处理
开发语言·python
_UMR_3 小时前
多线程场景的学习3,使用CountDownLatch
java·开发语言