C++温故补缺(十八):lock类
lock类
简单说明三个参数
lock_guard和unique_lock的第二个参数,是一个常数,一个标记tag。
Type | Effect(s) |
---|---|
defer_lock_t | do not acquire ownership of the mutex |
try_to_lock_t | try to acquire ownership of the mutex without blocking |
adopt_lock_t | assume the calling thread already has ownership of the mutex |
其中,lock_guard只能使用adopt_lock,而unique_lock三个都可以用。
defer_lock是一个标记,用来阻止对象在构造时获取mutex的所有权,也就是在构造时先不加锁,后面可以手动用lock加锁
Passing defer_lock to unique_lock's constructor, makes it not to lock the mutex object automatically on construction, initializing the object as not owning a lock.
try_to_lock类似try_lock都是非阻塞式地尝试获取锁,而adopt则是假设调用的线程已经对互斥量上锁,实际mutex对象并没有上锁,只是thread获取了mutex对象的所有权。
lock_guard
lock_guard是mutex的封装类,它提供了一种方便的RAII-style机制来获取一个mutex互斥量,到达持续加锁的目的。
(RAII-style是C++的一种资源管理机制,用其管理的对象,在对象构造时获取该对象的资源,在对象生命周期间控制对象资源的访问,最后在对象析构后释放对象资源。)
lock_guard完整格式:template <class Mutex> class lock_guard;
其中的Mutex不是mutex类,而是一个Basiclockable的类型,C++中有这几个类:
type |
---|
mutex |
recursive_mutex |
timed_mutex |
recursive_timed_mutex |
unique_lock |
例子:将之前的打印机模拟改成lock_guard同步
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex l;//打印机锁
string printstr;//模拟打印机装入信息
void printer(string str){
lock_guard<mutex> locker(l);//用lock_guard改写
printstr=str;
this_thread::sleep_for(chrono::seconds(1));//模拟线程被阻塞
for(int i=0;i<3;i++)
cout<<printstr<<endl;
}
int main(){
thread th1(printer,"Hello");
thread th2(printer,"world");
th1.join();
th2.join();
}
lock_guard不能中途解锁,不能复制/移动,但是效率高。
可以使用的参数:adopt_lock
lock_guard<mutex> locker(l,adopt_lock);
但是不能后续手动lock
unique_lock
参考:cplusplus
unique_lock也是一个模板类,拥有lock_guard的所有功能,但比lock_guard更强大。相比lock_guard构造时自动加锁析、构时解锁,unique_lock提供了加锁和解锁的接口,可以根据需求加锁解锁。
比如,在使用defer_lock参数后,unique_lock对象还可以后续手动上锁。如:
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex l;//打印机锁
string printstr;//模拟打印机装入信息
void printer(string str){
unique_lock<mutex> locker(l,defer_lock);//用unique_lock改写
locker.lock();//手动上锁
printstr=str;
this_thread::sleep_for(chrono::seconds(1));//模拟线程被阻塞
for(int i=0;i<3;i++)
cout<<printstr<<endl;
}
int main(){
thread th1(printer,"Hello");
thread th2(printer,"world");
th1.join();
th2.join();
}
defer_lock和adopt_lock的区别
defer_lock是暂时先不加锁,和未上锁的状态是一致的,而adopt_lock假设线程已经获取了锁,在mutex对象中,有个m_owns参数,也就是前面说的ownership,被谁获取了,这个值表示是否被调用的线程获取mutex对象的所有资源。但是并不是上锁的意思,上锁的参数是mutex对象的数据域的_lock值控制的,defer_lock和未上锁的状态一致,m_owns为false,data域的_lock值为0。当使用lock()手动上锁时,m_owns为true,data域的_lock值为1。
而adopt_lock,会使m_owns为true,但是_lock却是0,也就是线程拥有了mutex对象,但是并没有上锁,后续如果上锁的话,会导致死锁,因为上锁首先要获取锁,而锁已经被拥有了,也就导致死锁了。
实际操作:初始用defer_lock,后面手动上锁
可以看到_m_owns为false,__lock为0。接着step over
而adopt_lock:
_m_owns为true,所以说假设线程获取了资源,就是mutex的owns为true,但是__lock是0,也就是并没有上锁,如果此时继续调试,将会产生死锁:
本文来自博客园,作者:Tenerome,转载请注明原文链接:https://www.cnblogs.com/Tenerome/p/cppreview18.html