首页 > 编程语言 >C++多线程:mutex

C++多线程:mutex

时间:2024-11-01 19:59:18浏览次数:1  
标签:std unique lock 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()
    接收一个时间范围实参,若线程在指定时间段内没有获得锁,则被阻塞,并返回false
    while(!tmtx.try_lock_for(std::chrono::milliseconds(200))){
    }
    
  • try_lock_until()
    接收一个时间点实参,若线程在指定时间点没有获得锁,则被阻塞,并返回false

std::recursive_timed_mutex

定时递归互斥量

C++11提供RAII特性优化互斥量

与锁相关的Tag类

tag标记类,配合lock_guardunique_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
    lock_guard(mutex_type&, adopt_lock_t tag);
    
    与locking不同的是,互斥量已被当前线程锁住,此后锁对象交由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;
    
    被禁用,不可拷贝
  • 移动赋值
    unique_lock& operator=(unique_lock&& x)noexcept;
    
    转移对mutex对象的所有权,调用成功后,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()可以保证多个线程对该函数只调用一次

标签:std,unique,lock,C++,互斥,加锁,mutex,多线程
From: https://www.cnblogs.com/sgqmax/p/18521168

相关文章

  • C++多线程:condition_variable
    条件变量类似于pthread库中的pthread_cond_*()提供的功能,C++11标准提供了两种表示条件变量的类,分别是condition_variable和condition_variable_any,定义在头文件<condition_variable>中std::condition_variable当std::condition_variable对象调用wait()时,会阻塞当前线程,直到该s......
  • C++多线程:promise
    头文件包含:Providers类std::promisestd::packaged_taskFutures类std::futurestd::shared_futureProviders函数std::async()其他类型std::future_errorstd::future_errcstd::future_statusstd::launchstd::promise用来保存某一类型T的值,该值可以被future对......
  • C++多线程:package_task
    std::packaged_taskstd::packaged_task包装一个可调用对象,并允许获取该可调用对象计算的结果,可调用对象内部包含两个基本元素:1.被包装的任务任务是一个可调用对象,如函数指针或函数对象,该对象的执行结果会传递给共享状态2.共享状态用于保存任务的返回结果,并可通过future对象异......
  • C++标准库:chrono
    ratio先看一下ratio的定义template<intmax_tN,intmax_tD=1>classratio;ratio是由非类型参数定义的模板,用来定义一个比率N/D,如ratio<1,-2>表示-0.5标准库中定义的常用ratio类型typedefinition说明ratio<1,1000>std::milli1/1000ratio<1,1000000>std::mic......
  • C++标准库:random
    随机数生成设备随机数生成设备random_device,生成非确定性随机数,在Linux中通过读取/dev/urandom设备,Windows中使用rand_s重载了()运算符,每次调用会生成一个min()到max()之间的高质量随机数种子,若在Linux(UnixLike)下,可以使用这个生成高质量的随机数,可以理解为真随机数,windows下......
  • Chromium 中chrome.topSites扩展接口定义c++
    一、chrome.topSites使用 chrome.topSites API访问新标签页上显示的热门网站(即最常访问的网站)。不包括用户自定义的快捷方式。权限topSites您必须声明“topSites”扩展程序清单中授予使用此API的权限。{ "name":"Myextension", ... "permissions":[ ......
  • 06程序IPO模式与C++顺序结构
    一、程序IPO模式编程IPO是指输入、处理和输出(Input,Process,Output)的概念。在计算机编程中,IPO是一种常用的设计模式,用于描述程序的基本流程。具体来说,IPO指的是程序从接受输入数据开始,经过一系列处理计算,最终产生输出结果的过程。IPO模式的组成部分:-输入(Input):在这个阶段......
  • 复合结构(C++ Primer)
    复合结构(C++Primer)使用结构体和string使用结构体示例代码:#include<iostream>#include<string>usingnamespacestd;structperson{stringfn;stringsn;chargrade;intage;};intmain(){person*a=newperson;cout<<"what......
  • 【C++】——高效构建与优化二叉搜索树
    活着就意味必须要做点什么,请好好努力。——村上春树《地下》目录1、二叉搜索树BST1.1什么是二叉搜索树1.2BST的性能功能分析2、二叉搜索树的实现2.1BST框架2.2BST插入2.3BST搜索2.4BST删除2.5BST细节问题3、二叉搜索树遍历3.1中序遍历3.2前序遍历3.3......
  • UEC++中的GetClass和StaticClass函数
    GetClass()用途:GetClass() 是 UObject 类的一个实例方法,用于获取调用它的对象的类信息。返回类型:返回 UClass*,即指向调用对象的类的 UClass 对象的指针。使用场景:当你有一个 UObject 或其子类的实例,并且想要获取这个实例所属类的信息时,你会使用 GetClass()。例......