六、unique_lock取代lock_guard
unique是个类模板,工作中,一般lock_guard(推荐使用);
lock_guard取代了mutex的 lock() 和 unlock();unique_lock又取代lock_guard;qaq
unique和guard都是RAII风格的机制来的机制在作用域块的持续时间内拥有一个互斥量
- 创建lock_guard对象时,它将获取提供给他的互斥锁的所有权,当控制流离开lock_guard对象的作用域时,lock_guard析构并释放互斥量,
- 但是不能途中解锁。
- 创建即锁定
- unique_lock允许锁定包装器,它允许延迟锁定,限时深度锁定,递归锁定以及与条件变量一起使用,具有lock_guard的所有功能,能够应对更复杂的锁定需要。其有如下特点:
- 创建时可不锁定(指定第二个参数为std::defer_lock),而在需要时再锁定。
- 可以随时加锁解锁
- 作用域同lock_guard,析构时自动释放锁
- 不可复制,可移动
- 条件变量需要该类型作为参数。
unique_lock比lock_guard灵活很多,效率上差一些,内存占用多一些。
#include <thread> #include <iostream> #include <list> #include <mutex> using namespace std; class A { public: void inMsgRecvQueue() { for (int i = 0; i < 10000; ++i) { cout << "inMsgRecvQueue插入一个元素" << i << endl; std::lock(mymutex1, mymutex2); std::unique_lock<std::mutex> sbguard1(mymutex1, std::adopt_lock); //std::lock_guard<std::mutex> sbguard2(mymutex2, std::adopt_lock); msgRecvQueue.push_back(i); //mymutex2.unlock(); //...其他操作 //mymutex1.unlock(); } } bool outMsgProc(int &command) { std::lock(mymutex1, mymutex2); std::unique_lock<std::mutex> sbguard1(mymutex1, std::adopt_lock); //std::lock_guard<std::mutex> sbguard2(mymutex2, std::adopt_lock); if (!msgRecvQueue.empty()) { command = msgRecvQueue.front(); msgRecvQueue.pop_front(); //mymutex1.unlock(); //mymutex2.unlock(); return true; } //mymutex1.unlock(); //mymutex2.unlock(); return false; } void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; ++i) { bool re = outMsgProc(command); if (re == true) { cout << "outMsgRecvQueue执行,取出一个元素" << command << endl; } else { cout << "消息队列为空" << endl; } } } private: std::list<int> msgRecvQueue; std::mutex mymutex1; std::mutex mymutex2; }; int main() { A myobja; std::thread myoutobj(&A::outMsgRecvQueue, &myobja); //注意这里myobja用引用,才能保证线程里用的是同一个对象 std::thread myinobj(&A::inMsgRecvQueue, &myobja); myinobj.join(); myoutobj.join(); }
总结:能用lock_guard就用它,它不行就用unique_lock。
unique_lock可以带第二个参数
1、std::adopt_lock:表示互斥量已经被lock了(程序员必须把互斥量先lock,否则会报异常)
效果:假设线程调用方赢拥有了互斥的所有权,lock_graud与unique_lock都可以加这个参数,意义相同。
2、std::try_to_lock
我们会尝试用mutex中的lock()去锁定这个mutex,但如果没有锁定成功,也会立刻返回,并不会阻塞;
#include <thread> #include <iostream> #include <list> #include <mutex> using namespace std; class A { public: void inMsgRecvQueue() { for (int i = 0; i < 10000; ++i) { cout << "inMsgRecvQueue插入一个元素" << i << endl; std::unique_lock<std::mutex> sbguard1(mymutex1, std::try_to_lock); if (sbguard1.owns_lock()) { //判断是否拿到了锁 msgRecvQueue.push_back(i); //拿到了锁 } else { cout << "没能拿到锁" << endl; } } } bool outMsgProc(int &command) { mymutex1.lock(); std::unique_lock<std::mutex> sbguard1(mymutex1, std::adopt_lock); //std::lock_guard<std::mutex> sbguard2(mymutex2, std::adopt_lock); std::chrono::milliseconds dura(20000); std::this_thread::sleep_for(dura); if (!msgRecvQueue.empty()) { command = msgRecvQueue.front(); msgRecvQueue.pop_front(); return true; } return false; }private: std::list<int> msgRecvQueue; std::mutex mymutex1; };
通过这种方式让线程在没有拿到锁时执行其他事情
3、std::defer_lock:前提是你不能先lock(),否则汇报异常
defer_lock的意思是并没有给mutex枷锁:初始化了一个没有加锁的mutex
unique的成员函数(与defer_lock配合使用)
1、lock():加锁,加锁后,不用必须加unlock,unique_lock会帮助我们unlock()
2、unlock():解锁,如果想自己解锁,也可以使用unlock()
为什么需要unlock(),因为你锁住的代码段越少,执行越快,整个程序效率就越高。
也有人把搜头锁住的代码多少称为 粒度。一般用粗细描述。
a、锁住的代码少,粒度细;b、锁住的代码多粒度粗
3、try_lock():尝试给互斥量加锁,如果拿不到锁返回false,如果拿到了锁,返回true
4、release():返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系
如果原来mutex对象处于加锁状态
std::mutex mymutex1; std::unique_lock<std::mutex> myuniquemutex(mymutex1); std::mutex *pmutex = myuniquemutex.release(); //此时,你就需要自己unlock()了
如果myuniquemutex还有锁没被释放,那pmutex需要负责释放。
unique_lock所有权传递
std::mutex mymutex1;
std::unique_lock<std::mutex> sbguard1(mymutex1);
此时,sbguard1拥有mymutex1的所有权,
std::unique_lock<std::mutex> sbguard2(sbguard1); //非法 std::unique_lock<std::mutex> sbguard2(std::move(sbguard1)); //移动语义,现在相当于sbguard2与mymutex1绑定到一起了
class Test{ public: std::mutex mymutex1; std::uniuqe_lock<std::mutex> rtn_guard(){ std::unique_lock<std::mutex> tmpmutex(mymutex1); return tmpmutex; //因为tmpmutex为局部变量,函数返回局部变量时,会产生一个临 //时对象,并调用移动构造函数传递给外部变量 } std::uniuqe_lock<std::mutex> sbguard1 = rtn_guard(); }