双buffer模版

来源:互联网 发布:掉头发怎么办知乎 编辑:程序博客网 时间:2024/06/02 03:21
在实际工作中,经常会遇到这种情况:一些配置文件可能面临更改,希望更改完成后动态加载到进程中。要做到此,需要注意两个方面:
        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
       
    
        如何使用:真正的配置类需要自己实现,配置类的需要提供默认构造函数和带有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);

0 0