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

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;
            // 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

使用条件变量可以实现更精确的时间控制与线程同步:

#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,通过信号或线程触发回调:

#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提供了高精度的等待定时器:

#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模型,包括高性能定时器:

#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类:

#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. 简单事件循环定时器实现

#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

posted @ 2026-01-12 08:36  yangykaifa  阅读(6)  评论(0)    收藏  举报