在網絡服務等需要提供不間斷服務的程式中,程式的運作時更新通常都是一項正常任務,例如:
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;
}
}
}