淺談0/1切換

weixin_34279184發表於2015-04-30

 

前言:
  做過GUI開發的同學, 都知曉雙快取機制. 其過程為先把所有的場景和實體物件畫到一個備份canvas, 然後再把備份canvas的內容整個填充真正的畫板canvas中. 如果不採用雙快取機制, 你的畫面有可能會出現閃爍和抖動.
  究其原因是整個繪製過程, 包含清屏, 繪製場景和各個實體. 其耗時遠遠大於單個canvas的複製. 進而導致CPU寫canvas的速率小於LCD讀取canvas的速率. 這樣就出現閃爍的現象了.
  在後臺服務中, 也會遇到類似的情形: 當資料/資源需要更新時, 採用直接增量更新的方式代價大(耗時長, 阻塞服務可用/實時響應), 由此引入back buffer,做0/1切換.
  本文以"配置檔案熱載更新"為例, 著重介紹0/1切換的思路和優化技巧.

熱載更新:
  以往更新配置時, 往往需要重啟服務程式. 為了提高服務的可用性, 更方便運維.
  採取的改進方式是:
  1) 引入配置中心服務(ConfigServer)
  把模組的配置檔案擱置在ConfigServer中, 具體模組從ConfigServer中獲取(拉起/通知).
  2) 監控本地配置檔案變更
  程式模組通過定期輪詢/事件觸發的方式, 感知配置檔案是否發生變化, 若發生變化, 則重新載入.
  但無論採用何種方式, 勢必存在切換過程.

切換特點:
  把切換的雙方定義為前端和後端, 前端資源往往被N個執行緒訪問(靜態只讀), 後端資源往往是一個執行緒更新寫. 於是就形成了一個N讀1寫的格局.
  具體在c/c++實現時, 切換過程往往就是一個指標的重新賦值, 十分簡單.
  但問題也就隱藏在這了, 在切換後的舊資源銷燬過程中, 存在多執行緒的競態衝突風險.
  
  有人可能會提議, 如果對資源的訪問資源的切換相同的鎖保護, 就沒有這個問題. 但在低頻率切換的場景下, 加鎖帶來的效能損失, 有些得不償失.

無鎖0/1切換:
  是否存在無鎖的切換方式呢?
  1). 延遲銷燬
  工作執行緒持有並訪問舊資源控制程式碼時間不長, 可以設定一個時間視窗, 該時間視窗內屬於保護期, 禁止對舊資源進行銷燬.
  
  注: 在絕大多數場景下, 該方案滿足條件. 只是理論上, 不排除低概率事件.
  2). 帶引用計數的智慧指標切換
  我們藉助boost的shared_ptr來構建切換的小例子

#include <boost/shared_ptr.hpp>

#include <stdint.h>
#include <stdio.h>

class Config {
};

class DataCenter {
public:
    DataCenter() {
    }
    void init() {
        active_idx = 0;
        switchover[active_idx].reset(new Config());
    }
    // *) 切換函式, 由更新執行緒呼叫
    void swith() {
        uint32_t unactive_idx = (active_idx == 0) ? 1 : 0;
        uint32_t old_active_idx = active_idx;
    
        // *) 新資源ready    
        switchover[unactive_idx].reset(new Config());
        // *) 正式切換
        active_idx = unactive_idx;
        
        // *) 舊資源reset, 引入計數減一
        switchover[old_active_idx].reset();
    }

    // *) 訪問資源, 由前端執行緒呼叫
    boost::shared_ptr<Config> getConfig() {
        return switchover[active_idx];
    }

private:
    volatile uint32_t active_idx;
    // *) 切換陣列
    boost::shared_ptr<Config> switchover[2];
};

  巧用boost::shared_ptr內部有個原子計數器代理指標, 藉助RAII的思想完美的實現了無引用時的自動清理工作. 也避免了上述的競態衝突.

總結:
  在服務模組中的0/1切換有很多, 這邊簡述了下解決方案, 沒有細緻展開, 權當個人的學習筆記.

寫在最後:
  
如果你覺得這篇文章對你有幫助, 請小小打賞下. 其實我想試試, 看看寫部落格能否給自己帶來一點小小的收益. 無論多少, 都是對樓主一種由衷的肯定.

   

 

相關文章