C++ 併發編程(七):讀寫鎖(Read-Write Lock)

STL 和 Boost 都提供了 shared_mutex 來解決「讀者-寫者」問題。shared_mutex 這個名字並不十分貼切,不如 pthread 直呼「讀寫鎖」。函數

所謂「讀寫鎖」,就是同時能夠被多個讀者擁有,可是隻能被一個寫者擁有的鎖。而所謂「多個讀者、單個寫者」,並不是指程序中只有一個寫者(線程),而是說不能有多個寫者同時去寫。測試

下面看一個計數器的例子。ui

class Counter {
public:
  Counter() : value_(0) {
  }

  // Multiple threads/readers can read the counter's value at the same time.
  std::size_t Get() const {
    std::shared_lock<std::shared_mutex> lock(mutex_);
    return value_;
  }

  // Only one thread/writer can increment/write the counter's value.
  void Increase() {
    // You can also use lock_guard here.
    std::unique_lock<std::shared_mutex> lock(mutex_);
    value_++;
  }

  // Only one thread/writer can reset/write the counter's value.
  void Reset() {
    std::unique_lock<std::shared_mutex> lock(mutex_);
    value_ = 0;
  }

private:
  mutable std::shared_mutex mutex_;
  std::size_t value_;
};

shared_mutex 比通常的 mutex 多了函數 lock_shared() / unlock_shared(),容許多個(讀者)線程同時加鎖、解鎖,而 shared_lock 則至關於共享版的 lock_guardthis

shared_mutex 使用 lock_guardunique_lock 就達到了寫者獨佔的目的。atom

測試代碼:線程

std::mutex g_io_mutex;

void Worker(Counter& counter) {
  for (int i = 0; i < 3; ++i) {
    counter.Increase();
    std::size_t value = counter.Get();

    std::lock_guard<std::mutex> lock(g_io_mutex);
    std::cout << std::this_thread::get_id() << ' ' << value << std::endl;
  }
}

int main() {
  const std::size_t SIZE = 2;

  Counter counter;

  std::vector<std::thread> v;
  v.reserve(SIZE);

  v.emplace_back(&Worker, std::ref(counter));
  v.emplace_back(&Worker, std::ref(counter));

  for (std::thread& t : v) {
    t.join();
  }

  return 0;
}

輸出(仍然是隨機性的):指針

2978 1
4114 2
2978 3
4114 4
4114 6
2978 5

固然,對於計數器來講,原子類型 std::atomic<> 也許是更好的選擇。code

假如一個線程,先做爲讀者用 shared_lock 加鎖,讀完後忽然又想變成寫者,該怎麼辦?ip

方法一:先解讀者鎖,再加寫者鎖。這種作法的問題是,一解一加之間,其餘寫者說不定已經介入並修改了數據,那麼當前線程做爲讀者時所持有的狀態(好比指針、迭代器)也就再也不有效。rem

方法二:用 upgrade_lock(僅限 Boost,STL 未提供),能夠當作 shared_lock 用,可是必要時能夠直接從讀者「升級」爲寫者。

{
  // Acquire shared ownership to read.
  boost::upgrade_lock<boost::shared_mutex> upgrade_lock(shared_mutex_);

  // Read...

  // Upgrade to exclusive ownership to write.
  boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(upgrade_lock);

  // Write...
}

惋惜的是,我沒能給 upgrade_lock 找到一個頗具實際意義的例子。

相關文章
相關標籤/搜索