std::lock()
一次锁住
>=2
个互斥量 (谨慎使用)它不存在因为多个线程因为锁的顺序而导致的死锁风险
只有同时锁住才会继续向下执行(要么都锁住,要么都不锁)
std::lock(my_mutex1,my_mutex2,..);
my_mutex1.unlock();//解锁还是要继续写
my_mutex2.unlock();
std::adopt_lock
在用这个参数前,我们一定要记得先
lock
std::lock()
与std::lock_guard
结合使用有了
std::adopt_lock
std::lock_guard
就在构造时就不lock
了,析构正常unlock
std::lock(my_mutex1,my_mutex2);
std::lock_guard<std::mutex> a_guard(my_mutex1,std::adopt_lock);
std::lock_guard<std::mutex> b_guard(my_mutex2,std::adopt_lock);
std::unique_lock
unique_lock
是一个类模板 , 可以完全取代lock_guard
, 但一般用lock_guard
(推荐使用)
unique_lock
比lock_guard
灵活一点, 效率上差一点 , 内存多一点
std::unique_lock<std::mutex> myguard(my_mutex);
std::unique_lock<std::mutex> myguard(my_mutex,std::adopt_lock);//std::adopt_lock放在第二个,表示这个互斥量已经被lock了
std::try_to_lock
我们尝试用
lock()
去这个mutex
, 如果没有锁成功 , 就执行没锁成功的代码 , 然后再执行后续代码使用前提 : 这个线程不能自己先
lock
int command=0;
class A{
public:
void inmRQ(){
for(int i=1;i<=100000000;i++){
my_mutex1.lock();
mRQ.push(i);
my_mutex1.unlock();
cout<<"start"<<endl;
}
}
int outmRQtem(){
std::unique_lock<std::mutex> myguard(my_mutex1,std::try_to_lock);
if(myguard.owns_lock()){//拿到锁了
if( !mRQ.empty() ){
command=mRQ.front();
mRQ.pop();
}
}else{//没拿到锁
cout<<"I have no lock"<<endl;
}
return command;
}
void outmRQ(){
command=0;
for(int i=1;i<=100000000;i++){
command=outmRQtem();
cout<<command<<endl;
}
cout<<"end"<<endl;
}
private:
std::queue<int> mRQ;
std::mutex my_mutex1;
};
int main(){
A a;
thread myout(&A::outmRQ,ref(a));
thread myin(&A::inmRQ,ref(a));
myin.join();
myout.join();
return 0;
}
std::defer_lock
使用前提 : 不能自己先
lock
,否者会报异常初始化一个没有加锁的
mutex
std::unique_lock<std::mutex> myguard(my_mutex1,std::defer_lock);//将my_mutex1与myguard绑定到一起
myguard.lock();//这样就可以自己亲自加锁
myguard.unlock();//也可以自己亲自解锁,如果不写也会可以生命周期调用析构函数自动解锁
try_lock()
尝试给互斥量加锁 , 如果拿不到锁 , 则返回false , 如果拿到锁了 , 就返回true , 这个函数不会阻塞
一般和
std::defer_lock
搭配使用
std::unique_lock<std::mutex> myguard(my_mutex1,std::defer_lock);
if(myguard.try_lock()){//拿到锁了
if( !mRQ.empty() ){
command=mRQ.front();
mRQ.pop();
}
}else{//没拿到锁
cout<<"I have no lock"<<endl;
}
release()
返回它所管理的
mutex
对象指针 , 并释放所有权 即unique_lock
和mutex
不再有关系如果原来的
mutex
对象处于加锁状态 , 你有责任接管过来并负责解锁
std::unique_lock<std::mutex> myguard(my_mutex1);
if( !mRQ.empty() ){
command=mRQ.front();
mRQ.pop();
}
std::mutex *p=myguard.release();
p->unlock();
//my_mutex1.unlock();//release之后也可以这样用
unique_lock所有权的传递
方法一
std::unique_lock<std::mutex> myguard1(my_mutex1);//myguard1拥有my_mutex1的所有权
//这个所有权可以转移但不能复制,类似于智能指针
std::unique_lock<std::mutex> myguard2(std::move(myguard1));//移动所有权,与myguard2绑到一起,myguard1指向空
方法二
class A{
public:
std::unique_lock<std::mutex> my_move(){
std::unique_lock<std::mutex> tem(my_mutex1);
return tem;
}
void inmRQ(){
for(int i=1;i<=100000000;i++){
my_mutex1.lock();
mRQ.push(i);
my_mutex1.unlock();
cout<<"start"<<endl;
}
}
int outmRQtem(){
std::unique_lock<std::mutex> myguard=my_move();
if( !mRQ.empty() ){
command=mRQ.front();
mRQ.pop();
}
return command;
}
void outmRQ(){
command=0;
for(int i=1;i<=100000000;i++){
command=outmRQtem();
cout<<command<<endl;
}
cout<<"end"<<endl;
}
private:
std::queue<int> mRQ;
std::mutex my_mutex1;
};
粒度
有人把锁头锁住的代码多少称为粒度
锁住代码少 , 粒度细 ,效率高 , 反之多 ,效率低