std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性——即不支持递归地对 std::mutex 对象上锁。
std::mutex 成员函数
构造函数
std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。
lock()
1、如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
2、 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
3、 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
unlock()
解锁,释放对互斥量的所有权。
try_lock()
尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况:
1、 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。
2、 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。
3、 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。(这一条存疑,实测单线程内先lock再trylock,不会死锁,会有1ms以内的停顿,trylock返回false)
std::lock_guard
lock_guard是一个互斥量包装程序,它提供了一种方便的RAII(Resource acquisition is initialization )风格的机制来在作用域块的持续时间内拥有一个互斥量。
创建lock_guard对象时,它将尝试获取提供给它的互斥锁的所有权。当控制流离开lock_guard对象的作用域时,lock_guard析构并释放互斥量。
1、创建即加锁,作用域结束自动析构并解锁,无需手工解锁
2、不能中途解锁,必须等作用域结束才解锁
3、不能复制
std::unique_lock
unique_lock是lock_guard的升级版,它允许延迟锁定,限时深度锁定,递归锁定,锁定所有权的转移以及与条件变量一起使用。
1、创建时可以不锁定(通过指定第二个参数为std::defer_lock),而在需要时再锁定
2、可以随时加锁解锁
3、作用域规则同 lock_grard,析构时自动释放锁
4、不可复制,可移动
5、条件变量需要该类型的锁作为参数(此时必须使用unique_lock)
lock_guard 和unique_lock 并不管理 std::mutex 对象的生命周期,在使用 lock_guard 的过程中,如果 std::mutex 的对象被释放了,那么在 lock_guard 析构的时候进行解锁就会出现空指针错误。
std::mutex实测代码
#include <mutex>
#include <unistd.h>
std::mutex g_mutex;
pthread_t Tid[10];
void* threadfunc0(void*)
{
printf("threadfunc0\n");
std::unique_lock<std::mutex> locker(g_mutex);
// g_mutex.unlock();/*如果执行,则立即打印threadfunc1,after lock,不会等待5秒*/
sleep(5);
return nullptr;
}
void* threadfunc1(void*)
{
printf("threadfunc1\n");
std::unique_lock<std::mutex> locker(g_mutex);
if(g_mutex.try_lock() == true)//不会死锁,返回false,有1ms内的停顿
{
printf("try_lock\n");
g_mutex.unlock();
}
printf("threadfunc1,after lock\n");
return nullptr;
}
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
setbuf(stdout,nullptr);
pthread_create(&Tid[0],nullptr,threadfunc0,nullptr);
usleep(100);
pthread_create(&Tid[1],nullptr,threadfunc1,nullptr);
}
打印
threadfunc0
threadfunc1
(等待5秒)
threadfunc1,after lock