C++併發程式設計2——為共享資料加鎖(三)
阿新 • • 發佈:2019-01-04
正交——消除無關事務之間的影響,力求高內聚低耦合。
死鎖的概念略去不說,死鎖有可能發生在使用多個互斥量的場景下,也可能存在沒有使用互斥量的場景:
- 兩個執行緒都在等待對方釋放互斥量
- 兩個執行緒都呼叫了對方的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);
關注微信公眾號