C++并发编程2——为共享数据加锁(三)

正交——消除无关事务之间的影响,力求高内聚低耦合。
死锁的概念略去不说,死锁有可能发生在使用多个互斥量的场景下,也可能存在没有使用互斥量的场景:
  • 两个线程都在等待对方释放互斥量
  • 两个线程都调用了对方的join()函数
为了解决两个线程都在等待对方释放互斥量导致的死锁问题,C++11提供了若干机制:
  • std::lock()函数
  • std::unique_lock类
锁住所有互斥量 只要将互斥量作为参数传递给std::lock(),std::lock()就能够锁住多个互斥量。std::lock()并未指定解锁和上锁的顺序,其能够保证:
  • std::lock()执行成功时,所有互斥量都已经被上锁,并且没有死锁问题
  • std::lock()执行失败时,已被其上锁的互斥量都会被解锁
#include // std::cout #include // std::thread #include // std::mutex, std::lockclass 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 lock_a(lhs.m,std::adopt_lock); std::lock_guard lock_b(rhs.m,std::adopt_lock); std::swap(lhs.some_detail,rhs.some_detail); } }; template void swap(T& lhs,T& rhs); template<> void swap(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 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 lk(low_level_mutex); return do_low_level_stuff(); }void high_level_stuff(int some_param); void high_level_func() { std::lock_guard lk(high_level_mutex); high_level_stuff(low_level_func()); }void middle_level_stuff(int some_param); void middle_level_func() { std::lock_guard lk(middle_level_mutex); middle_level_stuff(high_level_stuff()); }int main() { high_level_func(); middle_level_func(); }

【C++并发编程2——为共享数据加锁(三)】按照层次锁的原则,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=https://www.it610.com/article/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);

C++并发编程2——为共享数据加锁(三)
文章图片
关注微信公众号

    推荐阅读