在使用mutex互斥量时,总会出现lock后没有unlock的情况,尤其是在判断分支中,某些被不常进入的分支忘记unlock,我们可以用RAII机制的模板类来解决忘记unlock的问题。
std::lock_gurad 是 C++11 中定义的模板类,定义如下:
template
#include
#include
#include
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的顺序和次数就不会出问题,但是没必要这样用)。
unique_lock也是 C++ 11 提供的模板了,完全代替lock_guard 的功能,并且更加灵活,功能更加丰富,但是相应的消耗比lock_guard大,效率低一些。定义如下:
template
unique_lock 有下面几种构造方式:
(一)unique_lock() noexcept;
这种默认的构造函数,构造出来的对象不管理任何 mutex 对象。
(二) explicit unique_lock (mutex_type& m);
这种构造函数构造出来的unique_lock 对象接管一个没有lock的mutex对象,并且在构造函数中调用mutex对象的lock函数,失败会阻塞,直到lock成功。由于加了explicit ,这种构造方式只能显式的构造。
#include
#include
#include
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
unique_lock (mutex_type& m, const chrono::duration
这种构造函数会调用try_lock_for,在rel_time这个时间段内尝试lock接管的mutex对象,超时会立即返回。
(七)
template
unique_lock (mutex_type& m, const chrono::time_point
这种构造函数会调用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的区别。