1. 避免嵌套锁, 如果每个线程都只占有一个锁, 则可以很大程度上避免死锁。
其死锁的情况是, 线程 1 依次获得 A 对象和 B 对象的锁, 然后决定等另一个线程的信号再继续, 从而先释放了 B 对象的的锁。
可是线程 2 需要同时拥有对象 A 和对象 B 的锁才能向线程 1 发信号。
从而导致, 线程 2 因无法获得对象 A 上的锁, 因而阻塞, 等待线程 1 释放对象 A 上的锁。 而同时线程 1 也一直阻塞, 等待线程 2 的信号, 因此不会释放对象 A 的锁。
2. 用固定的顺序获取锁
如果非要同时拥有多个锁, 同时无法拥有单个的锁, 那么最好的处理方式是以固定的顺序得到他们。典型的情况是在一个链表的结构中, 如果要取得节点 B 的锁, 那么需要先取得节点 A 的锁, 接着取得节点 C 的锁。
3. 设置优先级
当多个锁存在时, 设立优先级是个好主意,这样可以避免低优先级的 mutex 会先于高优先级的拥有锁。例如以下代码:
hierarchical_mutex high_level_mutex(10000);
hierarchical_mutex low_level_mutex(5000);int do_low_level_stuff();int low_level_func()
{lock_guard<hierarchical_mutex> lk(low_level_mutex);return do_low_level_stuff();
}void high_level_stuff(int some_param);void high_level_func()
{lock_guard<hierarchical_mutex> lk(high_level_mutex);high_level_stuff(low_level_func);
}void thread_a()
{high_level_func();
}hierarchical_mutex other_mutex(100);
void do_other_stuff();void other_stuff()
{high_levek_func();do_other_stuff();
}void thread_b()
{lock_guard<hierarchical_mutex> lk(other_mutex);other_stuff();
}
其中, thread_a 就会成功, 而 therad_b 就会运行出错。
为啥呢?
因为它首先会拥有 other_mutex 的锁,other_mutex 的优先级只有 100, 却在 high_level_mutex 前得到锁, 这违反了优先级, 所以 hierarchical_mutex 会报错并抛出一个异常, 然后退出程序。
上文中提到的 hierarchical_mutex 并不是标准库的一部分, 但是可以很容易的实现:
class hierarchical_mutex
{mutex internal_mutex;unsigned long const hierarchy_value;unsigned previous_hierarchy_value;static thread_local unsigned long this_thread_hierarchy_value;// second mutex must now be less than //that of the mutex already held
//in order for the check to passvoid check_for_hierarcy_violation(){if(this_thread_hierarchy_value <= hierarchy_value){throw std::logic_error("mutex hierarchical 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_hierarcy_violation();internal_mutex.lock();update_hierarchy_value();}void unlock(){this_thread_hierarchy_value = previous_hierarchy_value;internal_mutex.unlock();}bool try_lock(){check_for_hierarcy_violation();if(!internal_mutex.try_lock()){return false;}update_hierarchy_value();return true;}
};// initialized to the maximum value,
// so initially any mutex can be locked
thread_local unsigned longhierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);