整理lock的相关用法
[笔记整理]
多线程的情况下,如果使用mutex来完成锁的相关操作,则需要手动进行锁的加锁和解锁。如果出现忘记解锁的情况,可能会导致程序出现死锁等问题。
这种情况下使用标准库提共的lock相关的模板类比较合适。
标准库提供的lock主要有下面几个:
- lock_guard(c++11)
- unique_lock(c++11)
- shared_lock(c++14)
lock_guard
参考链接:
lock_guard在构造时会进行lock,析构时会调用unlock。不需要通过手动去做释放。
如果构造时,互斥资源被其他线程占有,则会阻塞直到获取锁。与mutex.lock()的效果相同。
示例:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int32_t gNum = 0;
void increase(int32_t id)
{
std::cout << id << " increase in\n";
std::lock_guard<std::mutex> lock(mtx);
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3)); //模拟一个耗时操作
while (i < 5000)
{
gNum++;
i++;
}
std::cout << id << " quit\n";
}
int main()
{
std::thread t1(increase, 1);
std::thread t2(increase, 2);
t1.join();
t2.join();
std::cout << gNum << std::endl;
return 0;
}
运行结果:
1 increase in
2 increase in
1 quit
2 quit
10000
unique_lock
参考链接:
构造函数
unique_lock存在下面几种构造函数。

基本用法:
定义一个lock对象,在构造函数中会调用mutex.lock()来获取锁,如果获取锁失败的话,也会阻塞当前线程,直到获取锁成功。
在lock对象被销毁时,会在析构函数中调用mutex.unlock()解锁。
也可以在对象被销毁之前,可以手动解锁再获取锁。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int32_t gNum = 0;
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
std::unique_lock<std::mutex> lock(mtx);
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
}
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
int main()
{
std::thread t1(increase, 1);
std::thread t2(increase, 2);
t1.join();
t2.join();
std::cout << gNum << std::endl;
return 0;
}
运行结果
1 increase in
2 increase in
1 quit
2 quit
10000
使用标签
- std::try_to_lock:在定义lock对象时,会调用mutex.try_lock(),然后通过owns_lock()函数来判断时候成功获取到锁。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int32_t gNum = 0;
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
return;
}
else
{
std::cout << id << " get lock ok\n";
}
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
int main()
{
std::thread t1(increase, 1);
std::thread t2(increase, 2);
t1.join();
t2.join();
std::cout << gNum << std::endl;
return 0;
}
运行结果,thread 2没有获取到锁之后返回了。
1 increase in
1 get lock ok
2 increase in
2 get lock fail
1 quit
5000
- std::defer_lock:延迟获取锁,需要定义对象后手动获取锁,在构造对象时,不会调用mutex的任意lock()函数。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int32_t gNum = 0;
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
}
else
{
std::cout << id << " get lock ok\n";
}
// int32_t i=0;
// std::this_thread::sleep_for(std::chrono::seconds(3));
// while (i < 5000)
// {
// gNum++;
// i++;
// }
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
int main()
{
std::thread t1(increase, 1);
std::thread t2(increase, 2);
t1.join();
t2.join();
std::cout << gNum << std::endl;
return 0;
}
设定defer_lock_t之后,定义lock对象的时候不会上锁。
1 increase in
1 get lock fail
1 quit
2 increase in
2 get lock fail
2 quit
0
手动调用lock的版本
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int32_t gNum = 0;
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
lock.lock();
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
}
else
{
std::cout << id << " get lock ok\n";
}
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
int main()
{
std::thread t1(increase, 1);
std::thread t2(increase, 2);
t1.join();
t2.join();
std::cout << gNum << std::endl;
return 0;
}
手动调用lock会阻塞执行的线程,直到获取到锁。
1 increase in
1 get lock ok
2 increase in
1 quit
2 get lock ok
2 quit
10000
手动调用try_lock()不会阻塞当前执行的线程,会直接返回结果true/false.
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
lock.try_lock();
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
return;
}
else
{
std::cout << id << " get lock ok\n";
}
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
2 increase in
2 get lock ok
1 increase in
1 get lock fail
2 quit
5000
-std::adopt_lock:可以接管已经上锁的mutex。
如果不设置标签std::defer_lock,在mutex已经被锁定的情况下,直接通过c++ unique_lock<mutex> lock(mtx)的方法再去获取锁,则会导致死锁,程序卡死。
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
if(mtx.try_lock())
{
std::cout << id << " get lock by mtx\n";
}
std::unique_lock<std::mutex> lock(mtx); //再次尝试去获取锁,用这种方式会死锁。
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
return;
}
else
{
std::cout << id << " get lock ok\n";
}
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
2 increase in
2 get lock by mtx
1 increase in
-->程序卡死在这里,thread 2死锁
使用std::adopt_lock接管被当前线程锁定的锁。
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
mtx.lock();
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock); //接管锁之后不需要通过mutex.unlock()去手动释放了
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
return;
}
else
{
std::cout << id << " get lock ok\n";
}
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
这样就不需要再通过手动unlock去释放mutex的锁了
1 increase in
1 get lock ok
2 increase in
1 quit
2 get lock ok
2 quit
10000
下面的代码是一个存在问题的代码,假设执行顺序是id 1->id 2的情况,thread 2会接管thread 1锁定的锁,造成资源。如果通过std::adopt_lock去接管不是由当前线程锁定的锁(当前线程未进行锁的锁定,或其他线程锁定的锁),活导致未定义行为。
void increase(int32_t id)
{
std::cout << id << " increase in\n";
{
if(mtx.try_lock())
{
std::cout << id << " get lock by mtx\n";
}
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock); //再次尝试去获取锁
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
return;
}
else
{
std::cout << id << " get lock ok\n";
}
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
} //退出作用域后lock自动销毁,锁会释放
std::cout << id << " quit\n";
}
使用adop_lock之后,后面再去尝试获取锁的线程会接管前面已经被锁定的锁。
zxy@ubuntu:~/test$ ./lock
1 increase in
1 get lock by mtx
1 get lock ok
2 increase in
2 get lock ok
2 quit
1 quit
10000
zxy@ubuntu:~/test$ ./lock
1 increase in
1 get lock by mtx
1 get lock ok
2 increase in
2 get lock ok ---->thread 1的锁被接管
2 quit
1 quit
9420 ----->两个线程
try_lock_for和try_lock_until
使用unique_lock的成员函数try_lock_for和try_lock_until,可以实现超时枷锁,在指定时间内没有获取到锁则返回。使用这两个函数时,需要传入timed_mutex,调用函数相当于调用mutex.try_lock_for/mutex.try_lock_until。
std::timed_mutex mtx;
int32_t gNum = 0;
void increase(int32_t id)
{
std::cout << id << " increase in\n";
std::unique_lock<std::timed_mutex> lock(mtx, std::defer_lock);
lock.try_lock_for(std::chrono::seconds(id));
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
}
else
{
std::cout << id << " get lock ok\n";
int32_t i=0;
std::this_thread::sleep_for(std::chrono::seconds(3));
while (i < 5000)
{
gNum++;
i++;
}
}
std::cout << id << " quit\n";
}
1 increase in
1 get lock ok --->获取锁OK后,总共会sleep 4s
2 increase in
2 get lock fail --->获取锁最多等待2s,因为此时线程1锁定中,获取锁失败
2 quit
1 quit
5000
shared_lock
- shard_lock
- shared_timed_mutex
shared_lock可以实现共享锁,需要使用shared_timed_mutex。
shared_lock在构造时会调用shared_timed_mutex的lock_shared()函数,析构时会调用unlock_shared()。
使用场景主要是多个线程同时读取同一个共享资源时,来使用共享锁。
#include <shared_mutex>
std::shared_timed_mutex mtx;
int32_t gNum = 100;
void read(int32_t id)
{
std::cout << id << " read in\n";
std::shared_lock<std::shared_timed_mutex> lock(mtx, std::defer_lock);
lock.lock();
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
}
else
{
std::cout << id << " get lock ok\n";
std::cout << id << " gNum = " << gNum << "\n";
}
std::cout << id << " quit\n";
}
1 read in
1 get lock ok
1 gNum = 100
1 quit
2 read in
2 get lock ok
2 gNum = 100
2 quit
100
shared_lock也可以调用try_lock(),try_lock_for(),try_lock_until()。
#include <shared_mutex>
std::shared_timed_mutex mtx;
int32_t gNum = 100;
void read(int32_t id)
{
std::cout << id << " read in\n";
std::shared_lock<std::shared_timed_mutex> lock(mtx, std::defer_lock);
lock.try_lock_for(std::chrono::seconds(id));
if(!lock.owns_lock())
{
std::cout << id << " get lock fail\n";
}
else
{
std::cout << id << " get lock ok\n";
std::cout << id << " gNum = " << gNum << "\n";
}
std::cout << id << " quit\n";
}

浙公网安备 33010602011771号