天天看點

C++11多線程互斥鎖的使用

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <mutex>
#include <thread>
#include <cstdarg>

// 參考:http://www.cnblogs.com/haippy/p/3284540.html

namespace thread_lock {

std::mutex g_io_mutex;

//------------------------------------------------------------------------

// 1.使用std::mutex互斥鎖來解決線程安全問題。

static void Func1() {
  g_io_mutex.lock();

  std::cout << "Enter thread: " << std::this_thread::get_id() << std::endl;
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
  std::cout << "Leave thread: " << std::this_thread::get_id() << std::endl;

  g_io_mutex.unlock();
}

static void Test1() {
  std::thread threads[10];
  for (std::thread& thread : threads) {
    thread = std::thread(Func1);
   }

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

//------------------------------------------------------------------------

template<typename T>
class Container {
public:
  void Add(const T& element) {
    mutex_.lock();
    elements_.push_back(element);
    mutex_.unlock();
  }

  // 2.同一個線程多次去擷取已經被該線程擷取的mutex鎖,如果使用std::mutex就會導緻程式進入死鎖。
  // (A)解決辦法可以使用std::recursive_mutex遞歸互斥量。
  // std::recursive_mutex 與 std::mutex 一樣,也是一種可以被上鎖的對象,但是和 std::mutex 不同的是,
  // std::recursive_mutex 允許同一個線程對用一個互斥量多次上鎖(即遞歸上鎖),來獲得對互斥量對象的多層所有權,
  // std::recursive_mutex 釋放互斥量時需要調用與該鎖層次深度相同次數的 unlock(),可了解為 lock() 次數和 unlock() 次數相同。
  // 而std::mutex效率更高。
  void AddForTestRecursive(const T& element) {
    mutex_.lock();
    Add(element);
    mutex_.unlock();
  }

  void Dump() const {
    mutex_.lock();
    for (const T& element : elements_) {
      std::cout << element << std::endl;
    }
    mutex_.unlock();
  }

private:
  std::list<T> elements_;
  //std::mutex mutex_;  // 導緻死鎖。
  //std::recursive_mutex mutex_;  // 編譯錯誤,因為Dump是const的,而函數中調用的mutex_的lock和unlock是非const的。

  // (B)mutable是為了突破const的限制而設定的。被mutable修飾的變量,将永遠處于可變的狀态,即使在一個const函數中,
  // 甚至結構體變量或者類對象為const,其mutable成員也可以被修改。
  mutable std::recursive_mutex mutex_;
};

static void Func2(Container<int>& container, int start, int end) {
  for (int i = start; i <= end; ++i) {
    container.AddForTestRecursive(i);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }
}

static void Test2() {
  Container<int> container;

  std::thread thread1(Func2, std::ref(container), 1, 10);
  std::thread thread2(Func2, std::ref(container), 100, 110);

  thread1.join();
  thread2.join();

  container.Dump();
}

//------------------------------------------------------------------------

// 3.區域鎖
// 區域鎖lock_guard使用起來比較簡單,除了構造函數外沒有其他member function,在整個區域都有效。
// 區域鎖unique_guard除了lock_guard的功能外,提供了更多的member_function,相對來說更靈活一些。

static void Func3() {
  std::unique_lock<std::mutex> lock(g_io_mutex);
  std::cout << "Enter thread: " << std::this_thread::get_id() << std::endl;
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
  std::cout << "Leave thread: " << std::this_thread::get_id() << std::endl;
}

static void Test3() {
  std::thread threads[10];
  for (std::thread& thread : threads) {
    thread = std::thread(Func3);
  }

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

}  // namespace thread_lock

//------------------------------------------------------------------------

int main() {
  Test1();
  //Test2();
  //Test3();

  return 0;
}