互斥量
C++11互斥锁定义在<mutex>
头文件中,提供了独占资源的特性
C++11头文件中定义的互斥量
互斥量 | 说明 |
---|---|
mutex | 基本互斥量 |
recursive_mutex | 递归互斥量 |
timed_mutex | 定时互斥量 |
recursive_timed_mutex | 递归定时互斥量 |
std::mutex
最基本的互斥量,提供了独占所有权的特性
默认初始化的mutex
对象是unlocked
的
不允许拷贝构造和移动构造
std::mutex
成员函数
lock()
若互斥量未被锁住,则当前线程将互斥量加锁,直到调用unlock()
释放锁
若互斥量被其他线程锁住,则当前线程被阻塞
若互斥量已被当前线程拥有,则产生死锁try_lock()
若互斥量未被锁住,则当前线程将互斥量加锁,直到调用unlock()
释放锁
若互斥量被其他线程锁住,则当前线程返回false
,而不会被阻塞
若互斥量已被当前线程拥有,则产生死锁unlock()
释放互斥量的所有权
如下示例,给变量count
加锁
std::mutex mtx;
if (mtx.try_lock()) {
++count;
mtx.unlock();
}
std::recursive_mutex
递归互斥量,允许一个线程对互斥量多次加锁,即递归加锁,来获取对互斥量对象的多层所有权
在释放递归互斥量时,需要保证与加锁次数相同,其他特性与std::mutex
大致相同
std::timed_mutex
定时互斥量,比std::mutex
多两个成员函数try_lock_for()
和try_lock_until()
try_lock_for()
接收一个时间范围实参,若线程在指定时间段内没有获得锁,则被阻塞,并返回false
while(!tmtx.try_lock_for(std::chrono::milliseconds(200))){ }
try_lock_until()
接收一个时间点实参,若线程在指定时间点没有获得锁,则被阻塞,并返回false
std::recursive_timed_mutex
定时递归互斥量
C++11提供RAII特性优化互斥量
与锁相关的Tag类
tag标记类,配合lock_guard
和unique_lock
使用,以标记锁类型
与STL中区分迭代器种类的设计类似,用于函数重载时提供参数类型支持
C++11提供了如下类型
std::adopt_lock_t
空标记类,定义了常量对象constexpr adopt_lock_t adopt_lock{};
std::defer_lock_t
空标记类,定义了常量对象constexpr defer_lock_t defer_lock{};
std::try_to_lock_t
空标记类,定义了常量对象constexpr try_to_lock_t try_to_lock{};
lock_guard
C++11中定义的模板类,与RAII相关,方便对互斥量加/释放锁,简化编写与mutex
相关的异常处理代码
lock_guard
对象并不负责管理Mutex对象的生命周期,只是简化了Mutex对象的上锁和解锁操作
类定义
template<typename Mutex>
class lock_guard;
构造函数
- locking
在构造时加锁explicit lock_guard(mutex_type&);
- adopting
与locking不同的是,互斥量已被当前线程锁住,此后锁对象交由lock_guard(mutex_type&, adopt_lock_t tag);
lock_guard
对象管理mtx.lock(); std::lock_guard<std::mutex> lck(mtx, std::adopt_lock);
拷贝构造
lock_guard(const lock_guard&)=delete;
不支持拷贝构造或移动构造
unique_lock
unique_lock
对象同样也不负责管理 Mutex 对象的生命周期
与RAII相关的模板类,相比于lock_guard
提供成员函数对锁进行操作,从而灵活度更强
一个unique_lock
对象只能和一个mutex
所对应,否则可能出现死锁
构造函数
-
default
unique_lock()noexcept;
-
locking
explicit unique_lock(mutex_type&);
传入mutex对象,并尝试调用
lock()
加锁 -
try_locking
unique_lock(mutex_type&, try_to_lock_t tag);
传入mutex对象,并尝试调用
try_lock()
加锁std::unique_lock mul(mlock, std::defer_lock); if(mul.try_lock()==true){ s+= i; }
-
deferred
unique_lock(mutex_type&, defer_lock_t)noexcept;
传入mutex对象,并不对互斥量加锁,需要使用
lock()/unlock
手动加/释放锁void work2(int& s){ for(int i=5001; i<=10000; i++>){ std::unique_lock<std::mutex> mul(mlock, std::defer_lock); mul.lock(); s+= i; mul.unlock(); // 可以不用unlock,unique_lock析构时会自动unlock } }
-
adopting
unique_lock(mutex_type&, adopt_lock_t);
传入一个已经被当前线程加锁的mutex对象,表示互斥量已加锁,不需要再重复加锁
该互斥量之前必须已经加锁,才可以使用该参数 -
locking_for
template<class Rep,class Period> unique_lock(mutex_type&, const chrono::duration<Rep,Period>&)
传入mutex对象,并尝试调用
try_lock_for()
加锁 -
locking_until
template<class Clock, class Duration> unique_lock(mutex_type&,const chrono::time_point<Clock,Duration>&)
传入mutex对象,并尝试调用
try_lock_until()
加锁
拷贝构造函数
unique_lock(const unique_lock&)=delete;
被禁用,不可拷贝
移动构造函数
unique_lock(unique_lock&& x);
移动构造,转移对mutex对象的所有权,调用成功后,x
不再管理任何mutex对象
赋值操作
- 赋值操作
被禁用,不可拷贝unique_lock& operator=(const unique_lock&)=delete;
- 移动赋值
转移对mutex对象的所有权,调用成功后,unique_lock& operator=(unique_lock&& x)noexcept;
x
不再管理任何mutex对象
std::unique_lock<std::mutex> rtn_ul(){
std::unique_lock<std::mutex> tmp(mlock);
return tmp;
}
void work(int& s){
for(int i=1; i<=5000; i++){
std::unique_lock<std::mutex> mul2= rtn_ul();
s+= i;
}
}
成员函数
lock()
try_lock()
try_lock_for()
try_lock_until()
unlock()
swap()
在unique_lock对象之间转移锁资源
owns_lock()
返回当前unique_lock对象是否管理锁
operator bool()
与owns_lock()
功能类似
mutex()
返回当前unique_lock对象所管理的mutex对象
std::release()
解除unique_lock和mutex的联系,返回原mutex对象指针,若之前的mutex已经加锁,则后面需要自己手动释放锁
std::unique_lock<std::mutex> mul(mlock);
std::mutex* m= mul.release();
s+= i;
m->unlock();
示例如下:
#include<iostream>
#include<mutex>
std::mutex mlock;
void work1(int& s){
for(int i=1; i<=5000; i++>){
std::unique_lock<std::mutex> mul(mlock, std::try_to_lock);
if(mul.owns_lock()==true){
s+= i;
}else{
// 执行没有共享内存的代码
}
}
}
void work2(int& s){
for(int i=5001; i<=10000; i++>){
std::unique_lock<std::mutex> mul(mlock, std::try_to_lock);
if(mul.owns_lock()==true){
s+= i;
}else{
// 执行没有共享内存的代码
}
}
}
int main(){
int ans= 0;
std::thread t1(work1, std::ref(ans));
std::thread t2(work2, std::ref(ans));
t1.join();
t2.join();
std::cout<< ans<<std::endl;
return 0;
}
<mutex>
头文件其他函数
提供的公共函数
std::lock()
可以同时对多个互斥量加锁std::try_lock()
尝试同时对多个互斥量加锁std::call_once()
若多个线程需要同时调用某个函数,call_once()
可以保证多个线程对该函数只调用一次