热加载需文件变更感知而非简单重读:先用stat()或getfileattributesex()校验mtime,再解析;linux用inotify需in_modify+in_moved_to监听并处理分片事件;windows服务应选readdirectorychangesw+iocp;配置替换须用std::atomic指针+raii确保线程安全。

配置文件修改后程序不生效,std::ifstream 读一次就卡住?
热加载不是“重新读一遍文件”就行,核心在于:程序得感知到文件变了。C++ 标准库没有内置文件监听机制,std::ifstream 打开后不会自动检测磁盘变化,反复 open() / close() 也不等于热加载——它只解决“读”,不解决“何时读”。
- 常见错误:在主循环里每秒
std::ifstream重读配置,但没校验文件是否真被修改(mtime 没变、内容没变也白读) - 正确做法:先用
stat()(Linux/macOS)或GetFileAttributesEx()(Windows)检查文件最后修改时间戳,仅当变化时才触发解析 - 注意
stat()的st_mtime精度问题:ext4 默认是秒级,如果配置在1秒内被改多次,可能漏掉中间版本 - 别用
fstream::is_open()判断是否需要重载——它只说明上次打开成功与否,和文件内容是否更新无关
用 inotify 监听 Linux 配置目录,但程序收不到事件?
inotify 是可靠选择,但默认行为容易踩坑:它只监控单层目录,且对符号链接、重命名、移动操作不敏感。
- 必须用
IN_MODIFY+IN_MOVED_TO组合监听,否则编辑器(如 vim)先写临时文件再rename()就会错过 -
inotify_add_watch()返回值要检查:返回 -1 表示失败,常见原因是目录权限不足或/proc/sys/fs/inotify/max_user_watches被耗尽 - 不要在
read()后直接处理事件——inotify事件可能分片到达,需循环read()直到返回 0 或EAGAIN - 监听路径必须是绝对路径;若配置文件在软链接目录下,
inotify不会跟随链接,得监听目标目录
Windows 上用 FindFirstChangeNotification,但服务进程收不到通知?
该 API 在 Windows 服务或无桌面会话的环境下常失效,根本原因是其依赖“I/O 完成端口+消息循环”模型,而服务默认没有 UI 消息队列。
- 不能直接在主线程调用
WaitForSingleObject()阻塞等待——这会让整个服务无法响应 SCM 请求 - 必须搭配线程 +
MsgWaitForMultipleObjects(),并手动泵消息(PeekMessage()+TranslateMessage()),否则通知永远不触发 - 更稳妥的选择是
ReadDirectoryChangesW()配合 I/O 完成端口(IOCP),它不依赖消息循环,适合后台服务 - 注意:
FindFirstChangeNotification()对子目录递归无效,要监听多级目录就得为每一层单独创建句柄
解析新配置时崩溃,std::shared_ptr 和旧配置对象冲突?
热加载本质是运行时替换数据,但 C++ 没有 GC,老对象生命周期管理不当就会引发 use-after-free 或竞态。
立即学习“C++免费学习笔记(深入)”;
- 避免直接全局替换指针:比如把
g_config从Config*改成新new Config,旧对象可能正被其他线程访问 - 推荐用原子指针 + RAII:
std::atomic<:shared_ptr>></:shared_ptr>存储当前配置,新配置构造完成后再exchange(),旧配置由引用计数自动析构 - 解析过程本身要线程安全:若用
rapidjson::Document,确保每次解析都在独立栈上,别复用同一个Document实例 - 切勿在信号处理函数(如
SIGUSR1)里做解析——信号上下文不保证malloc/new可重入,应只设标志位,由主循环检查
热加载最难的不是监听或解析,而是让“配置变更”这件事在多线程、长生命周期、异步 IO 的真实服务里不撕裂状态——时间戳比对、原子交换、解析隔离,每个环节断了都会导致配置看似更新了,实际行为却错乱。









