C++类构造函数中如何为已声明的unique_lock关联对应mutex
类内互斥量与对应unique_lock的关联实现
std::unique_lock默认构造出来是空锁状态,不会自动和任何互斥量建立关联,内部仅保存一个空的mutex指针,必须手动传入目标互斥量的地址才能完成绑定。
实现过程中需要重点规避两个问题:
- 绑定锁时不能直接触发加锁,不然构造类对象时就会把所有互斥量独占,直接造成死锁,需要用
std::defer_lock标记,仅建立关联、不主动获取锁的所有权 - 类成员的析构顺序和声明顺序完全相反,必须保证绑定了mutex的unique_lock,比被绑定的mutex更早析构,不然程序退出时unique_lock会访问已经销毁的mutex,触发未定义行为
调整类成员声明顺序
原类的成员声明顺序会导致析构时先销毁被unique_lock关联的mutex,必须调整为如下顺序:
#include <iostream> #include <mutex> #include <string> #define PHILO_NUM 5 class philosophers { private: // 先声明所有被关联的互斥量,保证它们的析构时机晚于对应的unique_lock std::mutex _mu[PHILO_NUM]; std::mutex _screenMutex; // 再声明对应的unique_lock对象,析构时会先销毁这部分unique_lock,再销毁前面的互斥量 std::unique_lock<std::mutex> _fork[PHILO_NUM]; std::unique_lock<std::mutex> _screen; public: philosophers(); };
构造函数内完成绑定
利用unique_lock的移动赋值特性,在循环和函数末尾完成对应关系绑定:
#include "philosophers.h" philosophers::philosophers() { for (int i = 0; i < PHILO_NUM; i++) { // 将_fork[i]绑定到对应的_mu[i],defer_lock表示仅关联、不主动加锁 _fork[i] = std::unique_lock<std::mutex>(&_mu[i], std::defer_lock); } // 将_screen绑定到屏幕互斥量,同样仅关联不主动加锁 _screen = std::unique_lock<std::mutex>(&_screenMutex, std::defer_lock); }
实现细节说明
- 绑定的时候必须传入互斥量的地址,unique_lock内部存储的是对应mutex的指针,无法直接绑定mutex的副本
std::defer_lock是必填参数,省略的话构造临时unique_lock时会立刻阻塞给对应mutex加锁,类构造完成时所有叉子、屏幕锁都会被独占,后续业务无法正常运行- 这种赋值方式完全合法:unique_lock是仅支持移动语义的类型,右侧构造的临时对象是右值,赋值时会直接把内部持有的mutex指针转移给左侧成员,没有额外拷贝开销
内容的提问来源于stack exchange,提问作者DavidHaGever
相关产品推荐
相关产品推荐

