通常不直接使用 mutex
,lock_guard
更加安全, 更加方便。
lock_guard
简化了 lock/unlock
的写法, lock_guard
在构造时自动锁定互斥量, 而在退出作用域时会析构自动解锁, 保证了上锁解锁的正确操作, 正是典型的 RAII
机制。
#include <thread>
#include <mutex>
std::mutex mutex_;
void func()
{
{
std::lock_guard<std::mutex> locker(mutex_); //出作用域自动解锁
//do some things...
}
mutex_.lock();
//do some things...
mutex_.unlock();
}
int main()
{
std::thread t(func);
t.join();
}
问题1:
像std::lock_guard<std::mutex> locker(mutex_);
这样同一句代码在多个位置出现,我以前不太了解的时候,不知道他们是不是管理的是同一个互斥锁mutex_
?
-
std::lock_guard<std::mutex> locker(mutex_);
这句话很具有误导性,看着像是一个拷贝构造函数的使用,像是新创建了一个锁locker
,并把一个互斥量mutex_
拷贝给他。实则并非如此,而是一个std::lock_guard
构造函数传入一个std::mutex
类的参数。 -
这句话传入的参数是一个互斥量
mutex_
,并不是一个lock_guard
对象,所以不是拷贝构造。 std::lock_guard类的构造函数禁用拷贝构造,且禁用移动构造。std::lock_guard类除了构造函数和析构函数外没有其它成员函数。 -
它是对互斥锁变量
mutex_
进行所有权获取,并不是每一处就重新创建一个锁进行锁住,这样毫无意义,因为如果是这样的话,每一个线程到了这句话的位置都会直接创建锁,不会有别人来抢夺。 -
正确理解:
std::lock_guard<std::mutex> locker(mutex_);
是对互斥锁变量mutex_
进行管理,在这句话后面的作用域内,代码都处于mutex_
上锁状态,别的位置代码获取这个锁mutex_
会失败。他们的目的就是为了保护同一个共享内存区域不会被多线程同时访问(同时读和写、或 同时写和写)。 -
在
std::lock_guard
对象构造时,传入的mutex
对象(即它所管理的mutex
对象)会被当前线程锁住。在lock_guard
对象被析构时,它所管理的mutex
对象会自动解锁,不需要程序员手动调用lock
和unlock
对mutex
进行上锁和解锁操作。 -
lock_guard
对象并不负责管理mutex
对象的生命周期,lock_guard
对象只是简化了mutex
对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个lock_guard
对象的生命周期内,它所管理的锁对象会一直保持上锁状态;而lock_guard
的生命周期结束之后,它所管理的锁对象会被解锁。程序员可以非常方便地使用lock_guard
,而不用担心异常安全问题。
std::lock_guard
在构造时只被锁定一次,并且在销毁时解锁。这段话摘自C++11中std::lock_guard的使用。
问题2:
std::lock_guard<std::mutex> locker(mutex_);
这句话是锁定mutex_
这个互斥量,避免其他线程获取这个互斥量吗? 还是说,只锁住这句话之后的代码块,避免别的线程访问该代码块的共享变量???
-
答:是锁住这个互斥量。因为一旦有一个线程的某段代码锁住了这个互斥量,其他线程就获取不了这个锁的权限了。使用同一个互斥量在不同的地方锁住,是因为这几个地方代码肯定会访问同一个变量(或者说共享内存区域),不然不需要使用锁。只有在多线程下才需要使用锁。哪怕只有一个地方使用锁,还是必要的,因为不同线程都在同一片代码块进行写操作时,也需要加锁防止同时在这个地方写造成写数据混乱。
-
在代码的多处都使用这同一句代码
std::lock_guard<std::mutex> locker(mutex_);
,这些地方都争抢这获取同一个互斥量mutex_
,肯定是这不同的地方都访问了同一片共享内存,或者限制访问资源,此时只能有一块代码能够获得这个锁,进而只有某一时刻只有该代码处能访问该资源。 -
简而言之,对于位置A和位置B对同一个互斥量
mutex
上锁std::lock_guard<std::mutex> locker(mutex_);
,作用就是: -
1.同一位置A处上锁,是防止线程1在位置A
写
的时候,线程2在位置A读
或写
该内存区域;或者,防止线程1在位置A读
的时候,线程2在位置A写
该内存区域; -
2.不同位置A和B都上锁,是防止线程1在位置A
读
的时候,线程2在位置B写
该内存区域,或者线程1在位置A写
的时候,线程2在位置B读
或写
该内存区域。 -
上面是我个人理解,如有错误,望各位大佬指正。
void func1(){
//位置A
std::lock_guard<std::mutex> locker(mutex_);
....
}
void func2(){
//位置B
std::lock_guard<std::mutex> locker(mutex_);
....
}