1. 程式人生 > >C++併發程式設計2——為共享資料加鎖(三)

C++併發程式設計2——為共享資料加鎖(三)

正交——消除無關事務之間的影響,力求高內聚低耦合。

死鎖的概念略去不說,死鎖有可能發生在使用多個互斥量的場景下,也可能存在沒有使用互斥量的場景:

  • 兩個執行緒都在等待對方釋放互斥量
  • 兩個執行緒都呼叫了對方的join()函式

為了解決兩個執行緒都在等待對方釋放互斥量導致的死鎖問題,C++11提供了若干機制:

  • std::lock()函式
  • std::unique_lock類

鎖住所有互斥量

只要將互斥量作為引數傳遞給std::lock(),std::lock()就能夠鎖住多個互斥量。std::lock()並未指定解鎖和上鎖的順序,其能夠保證:

  • std::lock()執行成功時,所有互斥量都已經被上鎖,並且沒有死鎖問題
  • std::lock()執行失敗時,已被其上鎖的互斥量都會被解鎖
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::lock

class some_big_object
{
public:
    some_big_object(int a) :x(a) {}
    void Print(){ std::cout << x << std::endl; }
 private
: int x; }; class X { private: some_big_object& some_detail; std::mutex m; public: X(some_big_object & sd):some_detail(sd){} friend void swap(X& lhs, X& rhs) { if(&lhs==&rhs) return; std::lock(lhs.m,rhs.m); std::lock_guard<std::mutex> lock_a(lhs.m,std
::adopt_lock); std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock); std::swap(lhs.some_detail,rhs.some_detail); } }; template<class T> void swap(T& lhs,T& rhs); template<> void swap<some_big_object>(some_big_object &x, some_big_object &y) { X a(x), b(y); swap(a, b); } int main () { some_big_object a(1),b(2); a.Print(), b.Print(); swap(a,b); a.Print(), b.Print(); return 0; }

上面一段程式碼使用了模板的偏特化特性,這裡不需要深究,只需要知道swap(a, b)最終會呼叫X類的swap友元函式。在該友元函式中,std::lock()函式鎖住兩個互斥量,std::lock_guard負責unlock兩個互斥量,如果不呼叫std::lock_guard(),需要手動unlock()。std::adopt_lock引數表示互斥量已經上鎖,這裡僅僅是不會重複上鎖。下面兩個例子起到相同作用。

// example 1
std::mutex mtx;
std::lock(mtx); // have to lock before the next sentence
std::lock_guard<std::mutex> guard(mtx, std::adopt_lock);

// example 2
std::mutex mtx;
std::lock(mtx);
mtx.unlock();

避免死鎖的一點建議

C++併發程式設計中給出了幾點避免死鎖的進階指導:

  • 1、避免巢狀鎖
  • 2、避免在持有鎖時呼叫使用者提供的程式碼
  • 3、使用固定順序獲取鎖
  • 4、使用鎖的層次結構

前三個建議看字面意思就可以了,我們這裡主要闡述鎖的層次結構。層次鎖需要遵守如下原則:

當代碼試圖對一個互斥量上鎖,在該層鎖已被低層持有時,上鎖是不允許的。

hierarchical_mutex high_level_mutex(10000);
hierarchical_mutex low_level_mutex(7000); 
hierarchical_mutex low_level_mutex(5000); 

int do_low_level_stuff();
int low_level_func()
{
  std::lock_guard<hierarchical_mutex> lk(low_level_mutex); 
  return do_low_level_stuff();
}

void high_level_stuff(int some_param);
void high_level_func()
{
  std::lock_guard<hierarchical_mutex> lk(high_level_mutex); 
  high_level_stuff(low_level_func()); 
}

void middle_level_stuff(int some_param);
void middle_level_func()
{
  std::lock_guard<hierarchical_mutex> lk(middle_level_mutex); 
  middle_level_stuff(high_level_stuff()); 
}

int main()
{
    high_level_func();
    middle_level_func();
}

按照層次鎖的原則,high_level_func()能夠正確執行,而middle_level_func()不能正確執行:

  • high_level_func()先獲取到高層級的鎖,然後獲取到低層級的鎖,符合原則
  • middle_level_func()先獲取低層級的鎖,然後獲取到高層級的鎖,不符合原則
class hierarchical_mutex
{
  std::mutex internal_mutex;
  unsigned long const hierarchy_value;
  unsigned long previous_hierarchy_value;
  static thread_local unsigned long this_thread_hierarchy_value; 
  void check_for_hierarchy_violation()
  {
    if(this_thread_hierarchy_value <= hierarchy_value)
    {
      throw std::logic_error(“mutex hierarchy violated”);
    }
}
  void update_hierarchy_value()
  {
    previous_hierarchy_value=this_thread_hierarchy_value;
    this_thread_hierarchy_value=hierarchy_value;
  }
public:
  explicit hierarchical_mutex(unsigned long value):
      hierarchy_value(value),
      previous_hierarchy_value(0)
  {}
void lock() {
    check_for_hierarchy_violation();
    internal_mutex.lock();
    update_hierarchy_value();
    }
  void unlock()
  {
    this_thread_hierarchy_value=previous_hierarchy_value; 
    internal_mutex.unlock();
  }
  bool try_lock()
  {
    check_for_hierarchy_violation();
    if(!internal_mutex.try_lock())
      return false;
    update_hierarchy_value();
    return true;
} };
thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);

關注微信公眾號