最轻量直接的配置监听方式是inotify,需监听IN_MODIFY和IN_MOVED_TO、处理IN_IGNORED标志、注意max_user_watches限制;轮询仅适用于低频场景;热重载应采用原子指针+RAII确保线程安全。

用 inotify 监听配置文件变更(Linux)
Linux 下最轻量、最直接的方式就是用 inotify,它不依赖第三方库,内核级通知,延迟低。但要注意:它只监控单个文件或目录,不能递归监听子目录里的配置(除非你显式添加每个路径),而且 inotify 实例有数量限制(/proc/sys/fs/inotify/max_user_watches),大项目里容易被耗尽。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
inotify_init1(IN_CLOEXEC)创建实例,避免 fork 后句柄泄露 - 对配置文件调用
inotify_add_watch(fd, "/etc/app.conf", IN_MODIFY | IN_MOVED_TO),必须同时监听IN_MODIFY(编辑保存)和IN_MOVED_TO(如 vim 用写新文件+rename 方式保存) - 读事件时用
read()一次性取完缓冲区,否则可能丢事件;每次读到struct inotify_event后,检查event->mask & IN_IGNORED—— 如果触发了重载又删了 watch,这个标志会告诉你 watch 已失效,别再用
std::filesystem::last_write_time 轮询是否可行?
可行,但只适合开发机或极低频场景。它不依赖系统 API,跨平台,代码简单,但轮询本身是反模式:要么延迟高(比如每秒查一次,变更后最多等 1s),要么 CPU 毛刺明显(设成 10ms 间隔,空转拉满)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 不要在主线程里裸写
while(true) { sleep(100ms); if (last_write_time != old) reload(); }—— 这会卡住整个程序响应 - 如果真要用轮询,把它塞进独立线程,并用
std::this_thread::sleep_for配合std::atomic_bool控制启停 - 注意
std::filesystem::last_write_time在某些文件系统(如 NFS、FAT32)上精度只有 2s,改完立刻 reload 可能读不到新时间
热重载时如何安全替换配置对象?
监听到变更后,直接 new 一个新配置对象再赋值给全局指针?危险。多线程下其他模块可能正读着旧配置,还没完成深拷贝就覆盖,导致读到半截数据。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用原子指针 + RAII 管理生命周期:
std::atomic<:shared_ptr>> g_config</:shared_ptr>,reload 时构造新shared_ptr,再用g_config.exchange(new_ptr) - 避免在 reload 函数里做耗时解析(如 YAML 解析)—— 应该先在监听线程里解析完,再原子交换,保证交换动作在微秒级完成
- 如果配置结构体含 raw 指针或自定义内存管理,别用
shared_ptr,改用std::unique_ptr+ 双缓冲(ping-pong buffer),交换时只换指针,老 buffer 等所有使用者离开后再销毁
Windows 下用 FindFirstChangeNotification 的坑
它比 ReadDirectoryChangesW 更轻,但只能监控目录,不能指定文件名;而且它不报告具体哪个文件变了,只告诉你“目录里有动静”,得自己比对文件列表。更麻烦的是:它不区分 CREATE 和 MODIFY,连 DELETE 都不单独上报,全挤在一次通知里。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 别对整个
config/目录用它,而是为每个关键配置文件单独开一个 notification handle(FindFirstChangeNotification(path_dir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE)),并把path_dir设为文件所在目录,不是文件本身 - 收到通知后,别急着 reload,先用
GetFileTime检查目标文件的最后写入时间是否真变了,防止误触发(比如编辑器临时生成 .swp 文件) - 每次调用
FindNextChangeNotification前,必须确保前一次通知已被处理完毕,否则会漏事件 —— 它不是队列,是状态翻转机制
真正难的从来不是监听到变化,而是 reload 过程中怎么让正在执行的请求不读到一半新、一半旧的配置。原子交换只是起点,你还得考虑配置字段的语义一致性 —— 比如超时时间从 5s 改成 1s,但某个长连接刚发完请求,还在等响应,这时候它该用哪个超时?这已经超出文件监听的范畴了。









