引入
今天看C++11的特性发现lock_guard这个锁对象,终于不用自己再管理锁的释放和手动资源管理了
锁对象的特质
std::lock_guard属于C++11特性,锁管理遵循RAII习语管理资源,
锁管理器在构造函数中自动绑定它的互斥体并加锁,在析构函数中解锁,大大减少了死锁的风险。
使用这个锁对象可以确保互斥锁将被解锁,即使发生异常,也不依赖异常处理机制来处理锁;
程序会在std::lock_guard生命周期内加锁和解锁,其中加锁和解锁分别在构造函数和析构函数中完成。
基本用法
#include <iostream>
#include <mutex>
#include <thread>
class Widget{
public:
Widget() = default;
~Widget() = default;
void fun(){
std::lock_guard<std::mutex> lock(lock_);
std::cout << "Widget::fun run" << std::endl;
}
private:
std::mutex lock_;
};
void TestThread1(Widget* w){
w->fun();
}
int main()
{
Widget* w = new Widget();
std::thread t1(&TestThread1, w);
t1.join();
return 0;
}
这个写法就是将我们的一个mutex锁放到lock_guard这个临时对象中,然后就等同于加锁了,这个锁对象会在它生命周期结束时释放锁;
好处
- 比手动上锁不仅仅是更方便,而且使用锁对象也更加安全,它可以保证你锁的释放(即使你忘记释放了,它也会在这个函数执行完了之后释放)
- 更容易接受异常(更不容易出现死锁的状态,每个锁都会确保将被解锁)
内部结构
template <class _Mutex>
class _LIBCPP_TEMPLATE_VIS _LIBCPP_THREAD_SAFETY_ANNOTATION(scoped_lockable)
lock_guard
{
public:
typedef _Mutex mutex_type;
private:
mutex_type& __m_;
public:
_LIBCPP_NODISCARD_EXT _LIBCPP_INLINE_VISIBILITY
explicit lock_guard(mutex_type& __m) _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability(__m))
: __m_(__m) {__m_.lock();}
_LIBCPP_NODISCARD_EXT _LIBCPP_INLINE_VISIBILITY
lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_THREAD_SAFETY_ANNOTATION(requires_capability(__m))
: __m_(__m) {}
_LIBCPP_INLINE_VISIBILITY
~lock_guard() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()) {__m_.unlock();}
private:
lock_guard(lock_guard const&) _LIBCPP_EQUAL_DELETE;
lock_guard& operator=(lock_guard const&) _LIBCPP_EQUAL_DELETE;
};
很明显,std::lock_guard在构造函数里调用互斥体的lock函数进行加锁,在析构函数里调用互斥体的unlock函数进行解锁。我们还可以看到std::lock_guard的拷贝构造函数和拷贝赋值运算符是私有的,因此std::lock_guard无法进行拷贝,它替我们完成了释放锁的工作(如果我们需要提前释放锁的话,只用delete这个对象)