在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
unique_lock 取代lock_guard
应用场景:两个线程A、B,其中A对队列添加元素,B移除元素。 unique_lock是个类模板,工作中,一般使用lock_gard(推荐使用),取代mutex的lock()和unlock()。unique_lock比lock_gard灵活许多,但是unique_lock效率差一点,并且内存占用多一点。 std::unique_lock<std::mutex> sbgard(mu_mutex); unique_lock 的第二个参数unique_lock比较灵活,主要是因为这个第二个参数。 lock_guard可以带第二个参数。 std::unique_lock<std::mutex> sbguard(my_mutex, std::adopt_lock); std::adopt_lock这个参数表示标记作用,unique_lock支持更多参数,表示互斥量已经被lock了 std::adopt_lock:表示这个互斥量已经被Lock了,必须把互斥量提前lock,否则会报异常。这个标记的效果:假设调用方线程已经拥有了互斥的所有权,也就是说已经lock成功了,通知lock_guard不需要在构造函数中Lock这个互斥量了。unique_lock也是同样的功能,就是不希望在unique_lock()的构造函数中再次lock了。 std::adopt_lock用adopt_lock的前提条件是要先给互斥量加锁,然后在后面的语句中不会再次lock(),即后续的std::lock_guard中才能使用std::adopt_lock这个参数,然后unique_lock自动unlock。 std::unique_lock<std::mutex> sbguard(my_mutex, std::adopt_lock); 如果另外一个线程A拿了锁之后,执行20s,然后这个线程B等待A释放锁。此时,A一直占用资源,B一直得不到资源,unique_lock如果一直拿不到锁,让它去先做别的事情,所以引入了try_to_lock。 std::try_to_lockstd::try_to_lcok会尝试用 mutex的lock()去锁定mutex,但是没有锁定成功,也会立即返回,并不会阻塞。用try_to_lock的前提是不能使用lock: std::unique_lock<std::mutex> sbguard(my_mutex, std::try_to_lock); a)注意,不能先去try_to_lock,尝试加锁。此时就有成功或者失败,尝试拿锁成功或者尝试拿锁失败。 b)如果拿到了锁,才能去操作共享数据(临界区),sbguard.owns_lock拿到锁,就不能操作共享数据,可以做一些别的事情。 c)A拿到锁,sleep 20s,此时B拿不到锁,一直在等待。线程不需要一直等资源,通过sbguard.owns_lock的值进行判断。 std::defer_lock前提:不能自己先lock,否者会出现异常。defer_lock的意思不需要给mutex加锁:初始化了没有加锁的mutex,没加锁的mutex,可以灵活地调用unique_lock的一些重要的成员函数。 std::unique_lock<std::mutex> sbguard(my_mutex, std::defer_lock); 之后需要手动 lock,unlock在析构函数中自动执行了。
unique_lock的成员函数a)lock(),加锁 b)unlock(),解锁 有lock就可以有unlock,不过在unique_lock中使用了锁,类的析构函数执行了解锁的操作。有的时候会出问题,但是实际上不稳定。有的时候可能临时需要处理一些其他事情。 lock() //处理一些共享数据 unlock() 也可以用unqiue_lock,编码变得更加灵活: std::unique_lock<std::mutex> sbguard(mutex); lock(); //处理一些共享数据 unlock(); //处理一些非共享数据 lock(); //// //// 此时unlock不必手工unlock,此时的unlock在unique_lock的析构函数中执行。 try_lock()尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,返回true,函数不阻塞。 if(sbguard.try_lock()==true) { //拿到锁了 访问共享数据 }else { //没拿到锁 } release()返回它所管理的mutex对象指针,并释放所有权,也就是说这个unique_lock和mutex不再有关系,严格区分unlock()和release()的区别。release()把绑在一起的东西解开了(解开关系),unlock()是解开锁头。 如果原来mutex对象处于加锁状态,需要接管过来并负责解锁。 std::unique_lock<std::mutex> sbguard(my_mutex); std::mutex *ptx = sbguard.release(); //此时sbguard和my_mutex的关系解除了,现在就有责任自己解锁my_mutex //如果自己不解锁,那么就卡住了 解锁语法:自己负责my_mutex的解锁。 ptx->nulock(); release()返回的是原始的mutex指针。 锁头锁住的代码的多少成为粒度,粒度一般用粗细来描述; a)锁住代码少,粒度细,执行效率高。 b)锁住代码多,粒度粗,执行效率低。 要学会尽量选择合适粒度的代码进行保护。 unique_lock所有权的传递unique_lock需要和mutex绑定到一起才是一个完整的unique_lock,应该是unique_lock需要管理一个mutex指针才能起作用。 所有权:指定unique_lock拥有mutex的所有权,unique_lock可以把所有用的所有权,可以转移给其他的unique_lock对象,所有权转移,mutex可以转移,但是不能复制 可以使用std::move(mutex) std::unique_lock<std::mutex> sbguard1(mutex); std::unique_lock<std::mutex> sbguard2(std::move(mutex)); 此时sbguard1为空,sbguard2接管了原来sbguard1和mutex的关系,其实就是新的unique_lock()指向了原来的mutex。 参考文献 https://blog.csdn.net/guanguanboy/article/details/100514731
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论