在实际工作中,经常会遇到这种情况:一些配置文件可能面临更改,希望更改完成后动态加载到进程中。要做到此,需要注意两个方面:
1.不断监测文件是否修改,如果有修改,则启动更新
2.如果配置文件在程序中对应的变量,一直会被访问,那么需要加锁进行,更新配置的时候可以占有锁进行更新操作,可以用读写锁实现。如果并发量非常大,那么在更新配置时,会对性能有一定的影响。
这里我实现了一种利用双buffer进行切换的机制来避免锁的使用,具体是:
1)进程运行时,访问一个配置对象,
2)同时另一个线程监测配置文件变化,如果更新,新建一个配置对象来更新配置文件,
3)更新完成后,切换进程访问对象,释放原配置对象
实现代码如下:
#ifndef _DOUBLE_BUF_TOOL_H
#define _DOUBLE_BUF_TOOL_H
#include <sys/stat.h>
#include <stdint.h>
//使用请标明来自 http://www.cnblogs.com/willbe/
//Object需要提供默认构造函数和带有config_path参数的Init函数
//DoubleBufTool会根据config_path中的touch文件的时间戳,来进行buf的切换
template <class Object>
class DoubleBufTool{
Object *objs_[2];
int valid;
string config_path_;
time_t modify_time_;
public:
DoubleBufTool(const std::string &config_path):config_path_(config_path){
objs_[0] = objs_[1] = NULL;
valid = 1;
modify_time_ = 0;
}
~DoubleBufTool(){}
int Run() {
std::string watch_file = config_path_;
if (watch_file[watch_file.size()] != '/'){
watch_file += "/";
}
watch_file += "touch";
while (true) {
struct stat file_stat;
int ret = stat( watch_file.c_str(), &file_stat );
if (ret != 0){
sleep(10);
continue;
}
if ( modify_time_ == file_stat.st_mtime){
sleep(10);
continue;
}
modify_time_ = file_stat.st_mtime;
int to_update = (valid+1) % 2;
if (objs_[to_update]){
delete objs_[to_update];
objs_[to_update] = NULL;
}
objs_[to_update] = new Object();
ret = objs_[to_update]->Init(config_path_);
if (ret != 0) {
delete objs_[to_update];
objs_[to_update] = NULL;
continue;
}
int to_del = valid;
valid = to_update;
sleep(1);//这个很重要,防止下一步delete时,有别的线程正在访问
if (objs_[to_del]){
delete objs_[to_del];
objs_[to_del] = NULL;
}
printf("config %s over", config_path_.c_str());
}
}
static void * StaticRun(void* tmp_this){
DoubleBufTool<Object>* my_this = (DoubleBufTool<Object> *)tmp_this;
my_this->Run();
}
Object *GetObj(){
return objs_[valid];
}
};
#endif
#define _DOUBLE_BUF_TOOL_H
#include <sys/stat.h>
#include <stdint.h>
//使用请标明来自 http://www.cnblogs.com/willbe/
//Object需要提供默认构造函数和带有config_path参数的Init函数
//DoubleBufTool会根据config_path中的touch文件的时间戳,来进行buf的切换
template <class Object>
class DoubleBufTool{
Object *objs_[2];
int valid;
string config_path_;
time_t modify_time_;
public:
DoubleBufTool(const std::string &config_path):config_path_(config_path){
objs_[0] = objs_[1] = NULL;
valid = 1;
modify_time_ = 0;
}
~DoubleBufTool(){}
int Run() {
std::string watch_file = config_path_;
if (watch_file[watch_file.size()] != '/'){
watch_file += "/";
}
watch_file += "touch";
while (true) {
struct stat file_stat;
int ret = stat( watch_file.c_str(), &file_stat );
if (ret != 0){
sleep(10);
continue;
}
if ( modify_time_ == file_stat.st_mtime){
sleep(10);
continue;
}
modify_time_ = file_stat.st_mtime;
int to_update = (valid+1) % 2;
if (objs_[to_update]){
delete objs_[to_update];
objs_[to_update] = NULL;
}
objs_[to_update] = new Object();
ret = objs_[to_update]->Init(config_path_);
if (ret != 0) {
delete objs_[to_update];
objs_[to_update] = NULL;
continue;
}
int to_del = valid;
valid = to_update;
sleep(1);//这个很重要,防止下一步delete时,有别的线程正在访问
if (objs_[to_del]){
delete objs_[to_del];
objs_[to_del] = NULL;
}
printf("config %s over", config_path_.c_str());
}
}
static void * StaticRun(void* tmp_this){
DoubleBufTool<Object>* my_this = (DoubleBufTool<Object> *)tmp_this;
my_this->Run();
}
Object *GetObj(){
return objs_[valid];
}
};
#endif
如何使用:真正的配置类需要自己实现,配置类的需要提供默认构造函数和带有config_path参数的Init函数,配置只能到文件夹级别,配置文件的名字是写死在程序中的。在main函数中启动代码如下:(进程启动后,在看到"config %s over"打印后表示配置成功加载,配置类才可以被访问,该进程此时才可以提供服务。)
int main(int ac, char **av){
DoubleBufTool<ConfigClass> *g_conf;//全局变量
g_conf= new DoubleBufTool<ConfigClass>("my_conf");
//另起线程,用于监测加载线程
pthread_t config_thread;
ret = pthread_create(&config_thread, NULL, DoubleBufTool<ConfigClass>::StaticRun, (void *)g_conf);
}
该配置文件对象在程序中使用:
//声明全局变量
extern DoubleBufTool<AdThreshold > *g_conf;
... ....
//调用代码
float res = g_conf->GetObj()->Fun(arg);
extern DoubleBufTool<AdThreshold > *g_conf;
... ....
//调用代码
float res = g_conf->GetObj()->Fun(arg);