1. 程式人生 > >c/c++ 多執行緒 boost的讀寫(reader-writer)鎖

c/c++ 多執行緒 boost的讀寫(reader-writer)鎖

多執行緒 boost的讀寫(reader-writer)鎖

背景:保護很少更新的資料結構時,c++標準庫沒有提供相應的功能。

例如:有個DNS條目快取的map,基本上很少有更新,大部分都是讀取,但是偶爾也會有更新,這種情況下,如果在讀取的函式里加上std::mutex就過於悲觀了,每次只能有一個執行緒讀取,但是想要的效果是,多個執行緒可以同時讀取。這個時候c++標準庫就顯得無能為力了。

boost庫就能登場了。

boost有個共享鎖:boost::shared_mutex和boost::shared_lock,用boost::shared_mutex代替std::mutex後,當有某一個執行緒讀取dns時,就鎖住了這個共享鎖,當第二個執行緒也要讀取時,這是第一個還沒讀完,也就是還沒有解鎖,如果用的是std::mutex的話,第二執行緒是無法進行讀取的,但是換成boost::shared_mutex後,第二個執行緒也可以讀取了,即便第一個讀取的執行緒還有結束。

但是也有限制,讀取操作可以多個執行緒同時讀取,但是某個時間點,有個執行緒要更新dns,是否可以更新取決於,這個時間點是否有讀的執行緒還沒有結束。

1,如果讀取的執行緒都結束了,也就是所有的共享鎖都被解鎖了,這時可以更新dns,並且在更新的同時,所有讀取的執行緒都要被阻塞;

2,如果讀取的執行緒還有任意一個沒有結束了,也就是說並不是所有的共享鎖都被解鎖了,這時,更新的執行緒就會被阻塞,直到,所有的讀取執行緒結束後,更新執行緒才會開始。

例子:

#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <functional>
#include <boost/thread/shared_mutex.hpp>

class dns_entry{

};
class dns_cache{
  std::map<std::string, dns_entry> entries;
  mutable boost::shared_mutex entry_mutex;
public:
  dns_entry find_entry(std::string const& domain)const{
    boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
    std::map<std::string, dns_entry>::const_iterator const it =
      entries.find(domain);
    return (it == entries.end()) ? dns_entry() : it->second;
  }
  void update_or_add_entry(std::string const& domain, dns_entry const& dns_details){
    std::lock_guard<boost::shared_mutex> lk(entry_mutex);
    entries[domain] = dns_details;
  }
};
int main(){
  dns_entry de;
  dns_cache cache1, cache2, cache3;
  std::thread t(&dns_cache::find_entry, std::ref(cache3), "aaa");
  t.join();
  std::thread t1(&dns_cache::update_or_add_entry, std::ref(cache1), "aaa", de);
  t1.join();
  std::thread t2(&dns_cache::find_entry, std::ref(cache2), "aaa");
  t2.join();
}

github原始碼

編譯方法:

g++ -g boost-shared-mutex.3.13.cpp -std=c++11 -L/home/ys/Downloads/boost_1_68_0/stage/lib -lboost_thread -lboost_system -pthread

編譯或者執行有問題的,請參考多執行緒 boost編譯與執行的坑

小知識點:

1,下面的&X::open_connection的用法,必須有&和this,雖然open_connection的引數列表為空。因為open_connection是類的成員方法,所以就必須繫結到這個類的某個具體物件上,所以才必須有this和&。&是為了告訴編譯器,這個方法不是類的static方法,而是類的成員方法。

2,std::ref(cache1),必須加上std::ref,否則編譯不通過。原因:如果不使用std::ref,就會複製一個cache1,這明顯不是想要的結果,想要的就是cache1,和bind函式原理一樣。

std::thread t1(&dns_cache::update_or_add_entry, std::ref(cache1), "aaa", de);

c/c++ 學習互助QQ群:877684253

本人微信:xiaoshitou5854