正交——消除无关事务之间的影响,力求高内聚低耦合。
死锁的概念略去不说,死锁有可能发生在使用多个互斥量的场景下,也可能存在没有使用互斥量的场景:
- 两个线程都在等待对方释放互斥量
- 两个线程都调用了对方的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);

网友评论