天天看點

多備份資料更新管理器模闆設計(C++ Template)

在網絡服務等需要提供不間斷服務的程式中,程式的運作時更新通常都是一項正常任務,例如:

    1)運作時修改一些參數配置;

    2)運作時修改一個算法子產品(動态庫);

為了在執行這些任務時,程式不停(仍使用舊配置或舊子產品運作),常見的解決方案包括:

    1)增加備援的配置項或子產品句柄的存儲空間(所有這些資料打包成一個結構體),程式運作時使用其中的一份;當需要更新時,在更新的過程中将新的配置(或子產品句柄)内容存儲至備用記憶體;加載完成後将運作時配置(或子產品句柄)指針指向新的記憶體位址。由于修改配置(或子產品句柄)的頻率遠小于這些内容的讀取頻率,是以一個線程執行更新操作即可。另外,即使外加有多個線程同時讀取,由于隻涉及到配置項(或子產品句柄)的整體結構體的這一個指針修改,讀線程讀到的值無非是“舊指針”和“新指針”二者之一(如果記憶體讀寫模型不支援這一點,或者在這種情況下可能有第三種,甚至第四種情況出現,本方案将不可用),是以程式仍能正常執行,并最終使用更新後的内容運作。

    2)程式通過一個主程序協調,具體任務由工作程序完成。外界通知這個主程序使用新的配置(或子產品),主程序便建立新的工作程序,這些新啟動的程序自然會使用新的配置(或子產品);當新建立的程序一切就緒之後,主程序便會通知其他程序結束,并在之後将新的任務委派給新建立的程序。Nginx的設計便是這種方案的一個典型。

這裡設計的一個C++模闆是上述解決方案1)的一種實作(完整地VS2008項目代碼在這裡):

//
// 資料清理函數指針類型
//
template<typename T>
struct DataCleaner {
    typedef void (*ClearFun)(T*);
};

template<typename T>
void _default_reset_fun_(T* t)
{
    memset(t, 0, sizeof(T));
};

//
// 多備份資料更新管理器
//
template<typename T, const size_t POOLSIZE=2, bool static_memory=true>
class LatestDataManager;

template<typename T, const size_t POOLSIZE>
class LatestDataManager<T, POOLSIZE, true>
{
public:
    typedef T value_type;

    LatestDataManager(){m_clf = _default_reset_fun_<T>;}
    ~LatestDataManager(){}

    T& GetCurrent(){ return m_elem[m_current]; }
    T& GetNext(){
        size_t next = (m_current+1)%POOLSIZE;
        m_clf(&m_elem[next]);
        return m_elem[next]; 
    }
    void UseLatest(){ m_current = (m_current+1)%POOLSIZE; }
    void SetClearFun(typename DataCleaner<T>::ClearFun f){ m_clf = f; }
private:
    T                           m_elem[POOLSIZE];
    size_t                      m_current;
    typename DataCleaner<T>::ClearFun    m_clf;
};

template<typename T, const size_t POOLSIZE>
class LatestDataManager<T, POOLSIZE, false>
{
public:
    typedef T value_type;

    LatestDataManager(){memset(m_elem, 0, POOLSIZE*sizeof(T*));}
    ~LatestDataManager(){}

    T* GetCurrent(){ return m_elem[m_current]; }
    T* GetNext(){
        size_t next = (m_current+1)%POOLSIZE;
        if(NULL!=m_elem[next]){
            delete m_elem[next];
            m_elem[next] = NULL;
        }
        m_elem[next] = new T;
        return m_elem[next]; 
    }
    void UseLatest(){ m_current = (m_current+1)%POOLSIZE; }
    void SetClearFun(typename DataCleaner<T>::ClearFun){}
private:
    T*                          m_elem[POOLSIZE];
    size_t                      m_current;
};
           

測試代碼:

template<typename T>
void _primdata_reset_fun_(T* t)
{
    *t = 0;
}
typedef LatestDataManager<int, 2, true>  LatestConfig;

template<typename T>
void _container_reset_fun_(T* t)
{
    if(t) t->clear();
}
typedef LatestDataManager<std::vector<std::string>, 2, false>  LatestMachines;


{
    LatestConfig g_cfg;
    g_cfg.SetClearFun( _primdata_reset_fun_< LatestConfig::value_type > );
    {// 更新者 ... ...
        g_cfg.GetNext() = 3;
        g_cfg.UseLatest();
    }
    {// 使用者 ... ...
        int config = g_cfg.GetCurrent();
        std::cout << config << std::endl;
    }
}

{
    LatestMachines g_lm;
    g_lm.SetClearFun( _container_reset_fun_< LatestMachines::value_type > );
    {// 更新者 ... ...
        std::vector<std::string>* machines = g_lm.GetNext();
        machines->push_back("192.168.0.1");
        machines->push_back("192.168.0.2");
        machines->push_back("192.168.0.3");
        g_lm.UseLatest();
    }
    {// 使用者 ... ...
        std::vector<std::string>* machines = g_lm.GetCurrent();
        for(std::vector<std::string>::iterator it=machines->begin(); it!=machines->end(); ++it)
        {
            std::cout << it->c_str() << std::endl;
        }
    }
}
           

繼續閱讀