给定路径,文件观察器会定期检查其可用性(在创建/删除的情况下)或时间戳(在文件存在且其内容已被修改的情况下)。下面的代码(取自here)正在进行中,我正在对其进行调整以支持单个文件(原始代码支持多个文件,其中给定路径是一个目录)。
class FileWatcher {
public:
std::string m_path;
std::chrono::duration<int, std::milli> m_delay;
std::unique_ptr<std::thread> m_watcher_thread;
FileWatcher(std::string path, std::chrono::duration<int, std::milli> delay);
void start(const std::function<void(std::string, FileStatus)>& action);
private:
std::unordered_map<std::string, std::filesystem::file_time_type> m_paths;
bool m_running = true;
bool contains(const std::string& key);
};
FileWatcher::FileWatcher(std::string path, std::chrono::duration<int, std::milli> delay) : m_path{ path }, m_delay{ delay } {
// std::this_thread::sleep_for(delay);
auto const p = std::filesystem::path(m_path);
if (std::filesystem::is_regular_file(p))
{
m_paths[p.string()] = std::filesystem::last_write_time(p);
}
else if (std::filesystem::is_directory(p))
{
for (auto const& file : std::filesystem::recursive_directory_iterator(m_path)) {
m_paths[file.path().string()] = std::filesystem::last_write_time(file);
}
}
else
{
throw std::exception("Given path to watch is expected to be a regular file or directory");
}
}
bool FileWatcher::contains(const std::string& key)
{
auto el = m_paths.find(key);
return el != m_paths.end();
}
void FileWatcher::start(const std::function<void(std::string, FileStatus)>& action) {
m_watcher_thread = std::unique_ptr<std::thread>(new std::thread(&action));
while (m_running) {
... // WIP Check status of file and call action
}
}
我按如下方式初始化我的观察者:
config_watcher = std::shared_ptr<FileWatcher>(new FileWatcher{ "./config.json" , std::chrono::milliseconds(5000) });
并通过调用启动它(
logger_spd
是使用文件接收器的spdlog记录器的std::shared_ptr
)
config_watcher->start([](std::string path, FileStatus status) -> void {
// Process only regular files, all other file types are ignored
if (!std::filesystem::is_regular_file(std::filesystem::path(path)) && status != FileStatus::erased) {
return;
}
switch (status) {
case FileStatus::created:
{
std::lock_guard<std::mutex> guard(config_update_mutex);
logger_spd->info("Configuration created");
break;
}
case FileStatus::modified:
{
std::lock_guard<std::mutex> guard(config_update_mutex);
logger_spd->info("Configuration modified");
break;
}
case FileStatus::erased:
{
std::lock_guard<std::mutex> guard(config_update_mutex);
logger_spd->info("Configuration deleted");
break;
}
default:
std::lock_guard<std::mutex> guard(config_update_mutex);
logger_spd->error("Unknown file status");
}
std::this_thread::sleep_for(config_watcher->m_delay);
}
);
初始代码是次优的,至少在我用作源的文章下面的评论中提到过,因为除其他问题外,
std::this_thread::sleep_for()
内部的FileWatcher::start()
调用阻止了程序的其余部分除非在单独的线程中实例化。这就是为什么我尝试修改代码以使其更像典型的工作线程,这意味着我想将 action
函数(FileWatcher::start()
的参数)传递给底层线程构造函数,然后管理类内的所有内容.
我在通过
FileWatcher::start()
函数将 lambda 转发到底层线程的构造函数时遇到了问题。
您不应该使用
action
启动线程,而是应该有一个不同的函数,其中包含循环并在循环内调用 action
并为线程使用该函数。
也许是这样的:
void FileWatcher::start(const std::function<void(std::string, FileStatus)>& action) {
m_watcher_thread = std::thread([action]() {
while (m_running) {
// Wait until something happens...
action(file, status);
}
});
}
请注意,我没有对线程对象使用指针,因为不需要。
“睡眠”也应该在循环内部,而不是在
action
函数中。