国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

借 shared_ptr 實現(xiàn) copy-on-write

Original 2016-11-07 15:43:53 644
abstract:場景:一個多線程的C++程序,24h x 5.5d運行。有幾個工作線程ThreadW{0,1,2,3},處理客戶發(fā)過來的交易請求,另外有一個背景線程ThreadB,不定期更新程序內部的參考數(shù)據。這些線程都跟一個hash表打交道,工作線程只讀,背景線程讀寫,必然要用到一些同步機制,防止數(shù)據損壞。這里的示例代碼用std::map代替hash表,意思是一樣的:typedef map<string,



場景:

一個多線程的C++程序,24h x 5.5d運行。有幾個工作線程ThreadW{0,1,2,3},處理客戶發(fā)過來的交易請求,另外有一個背景線程ThreadB,不定期更新程序內部的參考數(shù)據。這些線程都跟一個hash表打交道,工作線程只讀,背景線程讀寫,必然要用到一些同步機制,防止數(shù)據損壞。

這里的示例代碼用std::map代替hash表,意思是一樣的:

typedef map<string, vector<pair<string, int> > > Map;

map 的 key 是用戶名,value 是一個vector,里邊存的是不同stock的最小交易間隔,vector已經排好序,可以用二分查找。

我們的系統(tǒng)要求工作線程的延遲盡可能小,可以容忍背景線程的延遲略大。一天之內,背景線程對數(shù)據更新的次數(shù)屈指可數(shù),最多一小時一次,更新的數(shù)據來自于網絡,所以對更新的及時性不敏感。Map的數(shù)據量也不大,大約一千多條數(shù)據。

最簡單的同步辦法,用讀寫鎖,工作線程加讀鎖,背景線程加寫鎖。但是讀寫鎖的開銷比普通mutex要大,如果工作線程能用最普通的非重入Mutex實現(xiàn)同步,就不必用讀寫鎖,性能較高。我們借助shared_ptr實現(xiàn)了這一點:

class Mutex;
class MutexLockGuard;
class CustomerData
{
 public:
  CustomerData() : data_(new Map)
  { }
  ~CustomerData();
  int query(const string& customer, const string& stock) const
  {
    MapPtr data = getData();
    // data 一旦拿到,就不再需要鎖了。取數(shù)據的時候只有getData()內部有鎖,多線程并發(fā)讀的性能很好。
    // 假設用戶肯定存在
    const EntryList& entries = (*data)[customer];
    return findEntry(entries, stock);
  }
  void update(const string& customer, const EntryList& entries );
 private:
  typedef vector<string, int> EntryList;
  typedef map<string, EntryList> Map;
  typedef tr1::shared_ptr<Map> MapPtr;
  static int findEntry(const EntryList& entries, const string& key) const
  { /* 用 lower_bound 在 entries 里找 key */ }
  MapPtr getData() const
  {
    MutexLockGuard lock(dataMutex_);
    return data_;
  }
  MapPtr data_;
  mutable Mutex dataMutex_;
};

關鍵看CustomerData::update()怎么寫。既然要更新數(shù)據,那肯定得加鎖,如果這時候其他線程正在讀,那么不能在原來的數(shù)據上修改,得創(chuàng)建一個副本,在副本上修改,修改完了再替換。如果沒有用戶在讀,那么就能直接修改,節(jié)約一次拷貝。

void CustomerData::update(const string& customer, const EntryList& entries )
{
  MutexLockGuard lock(dataMutex_);
  if (!data_.unique())
  {
    MapPtr newData(new Map(*data_));
    data_.swap(newData);
  }
  assert(data_.unique());
  (*data_)[customer] = entries;
}

注意其中用了shared_ptr::unique()來判斷是不是有人在讀,如果有人在讀,那么我們不能直接修改,因為query()并沒有全程加鎖,只在getData()內部有鎖。shared_ptr::swap()把data_替換為新副本,而且我們還在鎖里,不會有別的線程來讀,可以放心地更新。

據我們測試,大多數(shù)情況下更新都是在原來數(shù)據上進行的,拷貝的比例還不到1%,很高效。更準確的說,這不是copy-on-write,而是copy-on-other-reading。

我們將來可能會采用無鎖數(shù)據結構,不過目前這個實現(xiàn)已經非常好,滿足我們的要求。


Release Notes

Popular Entries