C++多线程:mutex
互斥量
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()
接收一个时间范围实参,若线程在指定时间段内没有获得锁,则被阻塞,并返回falsewhile(!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()可以保证多个线程对该函数只调用一次

浙公网安备 33010602011771号