在服務端程式中,時間相關任務主要有三類:獲取當前時間、時區/日期計算、定時操作。Muduo 的選型原則:
gettimeofday()(微秒精度,x86-64 上為 vsyscall,無內核切換開銷)timerfd_* 系列(將定時器轉為檔案描述符,無縫融入 epoll,統一處理 IO 與超時事件)拒絕 sleep、alarm、timer_create 等基於信號的介面,因多線程下信號處理極其棘手;也拒絕阻塞式 nanosleep,違背非阻塞網絡編程原則。
typedef boost::function<void()> TimerCallback;
TimerId runAt(const Timestamp& time, const TimerCallback& cb); // 指定時刻
TimerId runAfter(double delay, const TimerCallback& cb); // 延遲執行
TimerId runEvery(double interval, const TimerCallback& cb); // 週期執行
// void cancel(TimerId timerId); // 尚未實現
所有回呼均在 EventLoop 所在線程執行,與網絡事件回呼同線程,避免併發問題。內部 TimerQueue 採用鏈表實現,適合定時器數量較少(<10)的場景;大量定時器建議改用二叉堆優化。
Muduo 重新實現了 Asio 教程中的四個定時器示例:
timer2:非阻塞定時timer3:回呼傳遞參數timer4:成員函數作為回呼timer5/6:多線程回呼與鎖優化以 timer4 為例,展示如何用 boost::bind 綁定成員函數實現週期性列印:
class Printer : boost::noncopyable
{
public:
Printer(EventLoop* loop) : loop_(loop), count_(0)
{
loop_->runAfter(1, boost::bind(&Printer::print, this));
}
~Printer() { std::cout << "Final count is " << count_ << "\n"; }
void print()
{
if (count_ < 5)
{
std::cout << count_ << " ";
++count_;
loop_->runAfter(1, boost::bind(&Printer::print, this));
}
}
private:
EventLoop* loop_;
int count_;
};
Linux 非即時系統,用戶態無法達成完全精確可控的計時;但透過 timerfd 納秒級精度、控制 CPU 負載,可在 99.99% 情況下滿足工程需求。閏秒、時鐘跳變等進階主題留待專題文章探討。