c++ 学习之 多线程(六)lock_guard 和 unique_lock

c++ 学习之 多线程(六)lock_guard 和 unique_lock

前言

在使用mutex互斥量时,总会出现lock后没有unlock的情况,尤其是在判断分支中,某些被不常进入的分支忘记unlock,我们可以用RAII机制的模板类来解决忘记unlock的问题。

正文

1.std::lock_guard

std::lock_gurad 是 C++11 中定义的模板类,定义如下:
template <class Mutex> class lock_guard;

#include<stdio.h>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
void fun(int& a)
{
	 for (int i = 0; i < 100000; i++)
	 {
	  lock_guard<mutex>m_guard(m);
	  a++;
	 }
}
int main()
{
         int a = 0;
	 thread t1(fun, ref(a));
	 thread t2(fun, ref(a));
	 t1.join();
	 t2.join();
	 printf("%d\n", a);
}

这种方法创建出来的lock_guard接管的mutex对象应该是未被加锁的,创建时调会在构造函数中对mutex对象加锁,生命周期结束调用析构函数时解锁。

for (int i = 0; i < 100000; i++)
  {
   m.lock();
   lock_guard<mutex>m_guard(m,adopt_lock);
   a++;
  }
}

也可以加adopt_lock参数,接管已被lock的mutex对象。注意,不管是哪种方式构造出的lock_guard,都不要再调用mutex对象的lock(),unlock()了(虽然只要保证lock和unlock的顺序和次数就不会出问题,但是没必要这样用)。

2.std::unique_lock

unique_lock也是 C++ 11 提供的模板了,完全代替lock_guard 的功能,并且更加灵活,功能更加丰富,但是相应的消耗比lock_guard大,效率低一些。定义如下:
template<class Mutex>class unique_lock;
unique_lock 有下面几种构造方式:
(一)unique_lock() noexcept;
这种默认的构造函数,构造出来的对象不管理任何 mutex 对象。
(二) explicit unique_lock (mutex_type& m);
这种构造函数构造出来的unique_lock 对象接管一个没有lock的mutex对象,并且在构造函数中调用mutex对象的lock函数,失败会阻塞,直到lock成功。由于加了explicit ,这种构造方式只能显式的构造。

#include<stdio.h>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
void fun(int &a)
{
for (int i = 0; i < 100000; i++)
 {
  unique_lock<mutex>unique_m(m);
  a++;
 }
 }
 int main()
 {
 int a = 0;
 thread t1(fun, ref(a));
 thread t2(fun, ref(a));
 t1.join();
 t2.join();
 printf("%d\n", a);
 }

(三)unique_lock (mutex_type& m, try_to_lock_t tag);
这样的构造函数,会在构造函数中调用mutex对象的try_lock函数,锁失败会立刻返回。
(四)unique_lock (mutex_type& m, defer_lock_t tag) noexcept;
这样构造出来的unique_lock 只是单纯的接管mutex对象,不会上锁。
(五)unique_lock (mutex_type& m, adopt_lock_t tag);
这种构造会接管一个已经lock的mutex对象,就是在构造函数中不再调用mutex的lock函数。
(六)
template <class Rep, class Period>
unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);

这种构造函数会调用try_lock_for,在rel_time这个时间段内尝试lock接管的mutex对象,超时会立即返回。
(七)
template <class Clock, class Duration>
unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);

这种构造函数会调用try_lock_until,在abs_time这个时间点之前尝试lock接管的mutex对象,超时立即返回。
(八)unique_lock (unique_lock&& x);
更换mutex所有权,新创建出来的unique_lock对象会接管参数中对象的mutex对象。

其中,(二)(五)两种在创建出对象后,mutex对象是lock状态的,(一)(四)是非lock状态的,其他的不定,不管任何mutex对象 ,一旦被 unique_lock对象接管后,不允许其他unique_lock对象接管了。

unique_lock 的其他成员函数
lock()
unlock()
try_lock()
try_lock_for()
try_lcok_until()
相较于lock_guard,unique_lock 可以随时的lock和unlock,更加灵活,try_lock_for()会在一段时间内尝试lock对象,超时立刻返回。
try_lcok_until()会在时间点之前尝试lcok对象,超时立刻返回。

unique_lock交换所有权
可以用std::move 或者临时对象来转换归属权

  unique_lock<mutex>unique_m(m);
  unique_lock<mutex>unique_n(n);
  unique_n = move(unique_m);
unique_lock<mutex>unique_m(m); 
unique_m = unique_lock<mutex>(n);

在交换所有权之前,左值会将自己所有的mutex对象unlock,然后接管右值的mutex对象,并且不会改变其状态。

unique_lock释放mutex对象
成员函数release可以释放所管理的mutex对象,调用之后,mutex对象不再归属当前unique_lock对象。但是并不改变mutex对象的状态,要区分release与unlock的区别。

发布了10 篇原创文章 · 获赞 6 · 访问量 410

猜你喜欢

转载自blog.csdn.net/weixin_45074185/article/details/104616554