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

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

引用 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,和智能指針相似。atom

#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_locklock_guard 原理相同,可是提供了更多功能(好比能夠結合條件變量使用)。
注意:mutex::scoped_lock 其實就是 unique_lock<mutex>typedef線程

至於 unique_locklock_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 進行同步,此例的輸出極可能變成:code

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

由於在下面這條輸出語句中:ip

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;
}
相關文章
相關標籤/搜索