C++ 併發編程(二):Mutex(互斥鎖)

原文轉載於:https://segmentfault.com/a/1190000006614695

多個線程訪問同一資源時,爲了保證數據的一致性,最簡單的方式就是使用 mutex(互斥鎖)。

引用 cppreference 的介紹:

The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.

Mutex 1

直接操作 mutex,即直接調用 mutex 的 lock / unlock 函數。

 

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>


std::mutex g_mutex;
int g_count = 0;

void Counter() {
  g_mutex.lock();

  int i = ++g_count;
  std::cout << "count: " << i << std::endl;

  // 前面代碼如有異常,unlock 就調不到了。
  g_mutex.unlock();
}

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

  // 創建一組線程。
  std::vector<std::thread> v;
  v.reserve(SIZE);

  for (std::size_t i = 0; i < SIZE; ++i) {
    v.emplace_back(&Counter);
  }

  // 等待所有線程結束。
  for (std::thread& t : v) {
    t.join();
  }

  return 0;
}

可惜的是,STL 沒有提供 boost::thread_group 這樣代表一組線程的工具,通過 std::vector 固然也能達到目的,但是代碼不夠簡潔。

Mutex 2

使用 lock_guard 自動加鎖、解鎖。原理是 RAII,和智能指針類似。

 

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex g_mutex;
int g_count = 0;

void Counter() {
  // lock_guard 在構造函數里加鎖,在析構函數裏解鎖。
  std::lock_guard<std::mutex> lock(g_mutex);

  int i = ++g_count;
  std::cout << "count: " << i << std::endl;
}

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

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

  for (std::size_t i = 0; i < SIZE; ++i) {
    v.emplace_back(&Counter);
  }

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

  return 0;
}

Mutex 3

使用 unique_lock 自動加鎖、解鎖。
unique_lock  lock_guard 原理相同,但是提供了更多功能(比如可以結合條件變量使用)。
注意:mutex::scoped_lock 其實就是 unique_lock<mutex>  typedef

至於 unique_lock  lock_guard 詳細比較,可移步 StackOverflow

 

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex g_mutex;
int g_count = 0;

void Counter() {
  std::unique_lock<std::mutex> lock(g_mutex);

  int i = ++g_count;
  std::cout << "count: " << i << std::endl;
}

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

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

  for (std::size_t i = 0; i < SIZE; ++i) {
    v.emplace_back(&Counter);
  }

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

  return 0;
}

Mutex 4

爲輸出流使用單獨的 mutex。
這麼做是因爲 IO 流並不是線程安全的!
如果不對 IO 進行同步,此例的輸出很可能變成:

 

count == count == 2count == 41
count == 3

因爲在下面這條輸出語句中:

std::cout << "count == " << i << std::endl;

輸出 "count == " 和 i 這兩個動作不是原子性的(atomic),可能被其他線程打斷。

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex g_mutex;
std::mutex g_io_mutex;
int g_count = 0;

void Counter() {
  int i;
  {
    std::unique_lock<std::mutex> lock(g_mutex);
    i = ++g_count;
  }

  {
    std::unique_lock<std::mutex> lock(g_io_mutex);
    std::cout << "count: " << i << std::endl;
  }
}

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

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

  for (std::size_t i = 0; i < SIZE; ++i) {
    v.emplace_back(&Counter);
  }

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

  return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章