C++11特性-多线程

news2025/1/12 20:43:16

多线程

        线程不是越多越好,每个线程有有一个独立的堆栈空间1M.线程切换需要保存很多中间状态

        商用程序的必须要求

并发的实现方法

        多进程并发:进程通信(同一电脑-文件、管道、共享内存、消息队列;不同电脑-socket)

        多线程并发: 单个进程,创建多个线程。通信(共享地址、指针、引用、全局变量)

        多线程开销  < 多进程开销

多线程数据共享

        只取数据:是安全的,不需要特别处理

        有读有写:程序如无其他处理,程序崩溃,简单处理是读写不同步

        共享数据保护:使用mutex互斥锁、信号量、边界条件

互斥量mutex与lock_guard

         某个线程锁住、操作数据、解锁,其他线程等待解锁、锁住、操作数据、解锁的步骤

        lock(),加锁,只有一个线程能加锁成功,成功就返回,没加锁成功就尝试加锁,unlock()解锁

        lock() 操作共享数据 unlock() 的操作步骤

        lock()与unlock()成对使用,先lock(),后unlock(),之间为数据操作

        std::lock_guard守卫者职能,可以同时替换lock与unlock,不能与lock或者unlock同时出现。lock_guard的构造函数执行lock,析构函数执行unlock,使用lock_guard的提前解锁使用{}作用域,结束其生命周期

        死锁:至少两个互斥量(即两个共享数据),两个线程都使用这两个互斥量,线程A先使用互斥A,线程2先使用互斥B

         死锁的解决方案:

                1.多个线程使用互斥量的顺序一样

                2.超时放弃

                3.std::lock(),一次锁住多个互斥量,一旦没锁住所有互斥量,就释放已锁住的互斥量

                        std::lock(mutex1,mutex2......)

        std::lock()与std::lock_guard配套使用        

                std::adopt_lock起一个标记作用,表示互斥量已将被lock,在构造时不再被lock

自带超时的互斥量 std::timed_mutex

        try_lock_for()尝试锁定互斥,若互斥在指定的时限时期中不可用则返回false, 否则返回true

        try_lock_until()尝试锁定互斥,若直至抵达指定时间点互斥不可用则返回false, 否则返回true

if (mutex.try_lock_for(100ms))
{//等待100ms,如果拿到,继续if流程if}
else{//没有拿到,继续else流程}

if (test_mutex.try_lock_until(nowTime + 10s))
{//等待到12.10.10秒,如果拿到,继续if流程}
else{//没有拿到,继续else流程}

unique_lock

        比lock_guard灵活,内存开销大点,效率低点

std::unique_lock<std::mutex> munique(mlock);

         第二参数unique_lock也可以加std::adopt_lock参数,表示互斥量已经被lock,不需要再重复lock。该互斥量之前必须已经lock,才可以使用该参数。

std::unique_lock<std::mutex> munique(mlock,adopt_lock);//标记mlock已将加锁

          第二参数std::try_to_lock避免一些不必要的等待,会判断当前mutex能否被lock,如果不能被lock,可以先去执行其他代码。这个和adopt不同,不需要自己提前加锁。

std::unique_lock<std::mutex> munique(mlock, std::try_to_lock);

         第二参数std::defer_lock这个参数表示暂时先不lock,之后手动去lock,但是使用之前也是不允许去lock。一般用来搭配unique_lock的成员函数去使用。下面就列举defer_lock和一些unique_lock成员函数的使用方法。

std::unique_lock<std::mutex> munique(mlock, std::defer_lock);
//当使用了defer_lock参数时,在创建了unique_lock的对象时就不会自动加锁
munique.lock();//手动加锁
//....
munique.unlock();//手动解锁,这里可以不用unlock,可以通过unique_lock的析构函数unlock

         try_lock()和上面的try_to_lock参数的作用差不多,判断当前是否能lock,如果不能,先去执行其他的代码并返回false,如果可以,进行加锁并返回true

std::unique_lock<std::mutex> munique(mlock, std::defer_lock);
if (munique.try_lock() == true) {}

        release()解除unique_lock和mutex对象的联系,并将std::mutex对象的指针返回出来。如果之前的mutex已经加锁,需在后面自己手动unlock解锁

std::unique_lock<std::mutex> munique(mlock);   // 这里是自动lock
std::mutex *m = munique.release();
//TODO	
m->unlock();

       unique_lock的所有权传递对越unique_lock的对象来说,一个对象只能和一个mutex锁唯一对应,不能存在一对多或者多对一的情况,不然会造成死锁的出现。所以如果想要传递两个unique_lock对象对mutex的权限,需要运用到移动语义或者移动构造函数两种方法,不能复制所有权。

//移动语句
std::unique_lock<std::mutex> munique1(mlock);
std::unique_lock<std::mutex> munique2(std::move(munique1));
// 此时munique1失去mlock的权限,并指向空值,munique2获取mlock的权限

//类的成员函数,返回临时变量,调用移动构造
std::unique_lock<std::mutex> rtn_unique_lock()
{
	std::unique_lock<std::mutex> tmp(mlock);
	return tmp;
}

std::unique_lock<std::mutex> munique2 = rtn_unique_lock();

条件变量::std::condition_variable

        条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:

                一个线程因等待"条件变量的条件成立"而挂起;

                另外一个线程使"条件成立",给出信号,从而唤醒被等待的线程。

        条件变量的使用总是和一个互斥锁结合在一起;通常情况下这个锁是std::mutex,并且管理这个锁只能是 std::unique_lock类,配合while使用(注意处理虚假唤醒)。

while(true) 
    {
        std::unique_lock<mutex> lock(mMutex);
        mCondition.wait(lock, [this] {
            if (!ls.empty()) {
                return true;
                }
                else {
                    return false;
                }
                });

        //走到这里,互斥锁一定锁住的
        //TODO
        lock.unlock();//unique随时解锁
        };

        上面提到的两个步骤,分别是使用以下两个方法实现:

                等待条件成立使用的是condition_variable类成员wait 、wait_for 或 wait_until。

                给出信号使用的是condition_variable类成员notify_one或者notify_all函数。

        

         线程的阻塞是通过成员函数wait()/wait_for()/wait_until()函数实现的,wait 导致当前线程阻塞直至条件变量被通知,若任何线程在 *this 上等待,则调用 notify_one 会解阻塞(唤醒)等待线程之一。

临界条件(windows临界区)

​​​​​​​        临界区(Critical Section) 保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线 程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操 作共享资源的目的。 临界区包含两个操作原语: EnterCriticalSection() 进入临界区 LeaveCriticalSection() 离开临界区 EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的 LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本 进程内的线程,而不可用来同步多个进程中的线程。

        类似于C++的mutex,mutex在同一线程不能被多次连续lock,std::recursive_mutex递归式互斥量能被连续多次lock()

        同一线程,windows的临界区可以被多次连续调用(进去几次,离开几次)

EnterCriticalSection(&winFlag);//进去临界区
EnterCriticalSection(&winFlag);//进去临界区
//TODO
LeaveCriticalSection(&winFlag);//离开临界区
LeaveCriticalSection(&winFlag);//离开临界区

使用临界区的步骤:

        a申请一个临界区变量  CRITICAL_SECTION gSection;

        b初始化临界区  InitializeCriticalSection(&gSection);

        c使用临界区 EnterCriticalSection(&gSection);

        d离开临界区LeaveCriticalSection(&gSection);

        e释放临界区 DeleteCriticalSection(&gSection);

class  C {
public:
    C() {
        InitializeCriticalSection(&winFlag);//初始化临界区
    }
    ~C() {
        DeleteCriticalSection(&winFlag);//释放临界区
    }

public:
    void test() {
        for (int i = 0; i < 2000; ++i) {
            EnterCriticalSection(&winFlag);//进去临界区
            cout << "插入一个元素 i = " << i << endl;
            ls.push_back(i); 
            LeaveCriticalSection(&winFlag);//离开临界区
        }
    }
}

  线程池

        同一调度管理,循环利用的线程的方式,更加稳定

        实现方式:在程序启动时,一次性创建多个线程,一般来说程序最多开2000个线程,通常在500线程内

        使用场景:

                1.单位时间内处理任务频繁而且任务处理时间短;
                2. 对实时性要求较高。如果接受到任务后在创建线程,可能满足不了实时要求,因此必须采用线程池进行预创建。

        线程池的组成:

                线程池管理器:初始化和创建线程,启动和停止线程,调配任务;管理线程池

                工作线程:线程池中等待并执行分配的任务

                任务接口:添加任务的接口,以提供工作线程调度任务的执行。

                任务队列:用于存放没有处理的任务,提供一种缓冲机制,同时具有调度功能,高优先级的任务放在队列前面

        

 

        

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JSP课设:学生信息管理系统(附源码+调试)

JSP学生信息管理系统 &#xff08;1&#xff09;登录模块&#xff1a;本系统提供用户和管理员两种登陆方式&#xff0c;用户通过输入账号和密码&#xff0c;进行登录。 &#xff08;2&#xff09;注册模块&#xff1a;注册者输入账号和密码&#xff0c;并选择对应的权限级别&a…

交互式仪表板!Python轻松完成!⛵

&#x1f4a1; 作者&#xff1a;韩信子ShowMeAI &#x1f4d8; 数据分析实战系列&#xff1a;https://www.showmeai.tech/tutorials/40 &#x1f4d8; 本文地址&#xff1a;https://www.showmeai.tech/article-detail/410 &#x1f4e2; 声明&#xff1a;版权所有&#xff0c;转…

2022年iFLYTEKA.I.开发者大赛疫情微博情绪识别挑战赛

自然语言技术 零基础入门NLP - 新闻文本分类 基于word2vec的word相似度 疫情微博情绪识别挑战赛自然语言技术背景一、赛事任务二、使用步骤1.README2.数据下载3.模型训练及保存4.模型预测5.比赛结果背景 疫情发生对人们生活生产的方方面面产生了重要影响&#xff0c;并引发了…

写一个计算器【后缀表达式】(C++)

前言&#xff1a; 闲来无事&#xff0c; 用后缀表达式写了个计算器。。。 支持加()、减(-)、乘(*)、除(/)、乘方(^) 啥是后缀表达式&#xff1a; 波兰逻辑学家卢卡西维奇发明的表示表达式的方法 后缀式即逆波兰式&#xff0c;是波兰逻辑学家卢卡西维奇&#xff08;&#…

mysql根据父节点递归查询所有子节点,List转树形结构工具类

经常有业务需要递归查询MySQL树形结构某一节点的所有子节点&#xff0c;每次从网上扒拉找都得找半天&#xff0c;索性就自己总结一些自己遇到的比较好用的一些方法。 SQL方法一&#xff1a; SELECT* FROM(SELECTt1.*,IF( FIND_IN_SET( parent_id, parent_ids ) > 0, paren…

2022年先进封装行业研究报告

第一章 行业概况 封装为半导体产业核心一环&#xff0c;主要目的为保护芯片。半导体封装测试处于晶圆制造过程中的后段部分&#xff0c;在芯片制造完后&#xff0c;将晶圆进行封装测试&#xff0c;将通过测试的晶圆按需求及功能加工得到芯片&#xff0c;属于整个 IC 产业链中技…

Android平台GB28181设备接入端如何实时更新经纬度实现国标平台侧电子地图位置标注

技术背景 我们在做GB28181设备接入端的时候&#xff0c;其中有个功能&#xff0c;不难但非常重要&#xff1a;那就是GB28181实时位置的订阅&#xff08;mobileposition subscribe&#xff09;和上报(notify)。 特别是执法记录仪、智能安全帽、车载终端等场景下&#xff0c;现…

mysql-基础SQL语句CRUD

文章目录MySQL基础1&#xff0c;数据库相关概念1.1 数据库1.2 数据库管理系统1.3 常见的数据库管理系统1.4 SQL2&#xff0c;MySQL2.1 MySQL安装2.1.1 下载2.1.2 安装(解压)2.2 MySQL卸载2.3 MySQL配置2.3.1 添加环境变量2.3.2 新建配置文件2.3.3 初始化MySQL2.3.4 注册MySQL服…

国家涉及身份安全新规解读 | 《关键信息基础设施安全保护要求》

2022 年11 月 7 日&#xff0c;《信息安全技术关键信息基础设施安全保护要求》&#xff08;GB/T39204-2022&#xff09;国家标准发布。作为关键信息基础设施安全保护标准体系的构建基础&#xff0c;该标准将于 2023 年 5 月 1 日正式实施。 该标准提出了以关键业务为核心的整体…

软件测试内容的要点

软件测试内容的要点 20大的召开后&#xff0c;其中提出着力点坚持把发展经济的着力点放在实体经济上&#xff0c;加快建设制造强国、质量强国、航天强国、交通强国、网络强国、数字中国。 随着网络的快速发展&#xff0c;网络和数字已经呈现在大众的视野&#xff0c;软件产品…

2022-12-27 不下载Android Studio直接下载SDK、Platforms-tools、NDK

文章目录全平台支持&#xff01;&#xff01;&#xff01;1. 下载Command line tools only2. 解压command only3. 下载SDK, Platforms-tools, NDK全平台支持&#xff01;&#xff01;&#xff01; 1. 下载Command line tools only 官网链接&#xff1a;https://developer.and…

【电商】FMS财务管理系统---数据流转模块组成

继了解FMS财务管理系统定义之后&#xff0c;如何设计一个FMS系统成了新的问题&#xff0c;笔者在此介绍了FMS的业务逻辑和结构&#xff0c;希望对读者有所帮助。 在此还是要强调一下&#xff0c;虽然这里称之为“财务系统”&#xff0c;但和专业的财务软件有区别&#xff0c;只…

零售收银软件太难挑!实测市面上最受欢迎的收银软件,第一个好用

零售收银软件千千万&#xff0c;常常让商户老板挑得眼花缭乱&#xff0c;不少老板反馈&#xff0c;不知道怎么挑选收银系统软件才能不踩雷。本期小编收集了市场受欢迎度较高的五个零售收银软件&#xff0c;整理了它们各自的优势和劣势&#xff0c;供大家参考。 第一名&#xff…

Appium基础 — APP模拟手势高级操作(一)

APP模拟手势的动作都被封装在TouchAction类中&#xff0c;TouchAction是AppiumDriver的辅助类&#xff0c;主要针对手势操作&#xff0c;如滑动、按、拖动等&#xff0c;原理是将一系列的动作放在一个链条中发送到服务器&#xff0c;服务器接受到该链条后&#xff0c;解析各个动…

碳酸氢锂溶液除钙镁

锂及其盐类是国民经济和国防建设中具有重要意义的战略物资&#xff0c;也是与人们生活息息相关的能源材料。而碳酸锂作为锂盐的基础盐&#xff0c;是制取锂化合物和金属锂的原料&#xff0c;可作铝冶炼的电解浴添加剂&#xff0c;亦可用于合成橡胶、染料、半导体等方面。电池级…

当我把用Python做的课堂点名系统献给各科老师后,再也没挂过科

文章目录起因准备工作效果展示代码实战最后起因 刚上大学的表弟问我&#xff0c;大学准备好好玩玩&#xff0c;问我有没有什么不挂科的秘诀。 哎&#xff0c;这可就问对人了&#xff0c;要想不挂科&#xff0c;先把老师贿赂好&#xff0c;当然&#xff0c;咱们说的贿赂不是送…

全国首个月子中心服务等级划分团体标准的实施拉开月子中心行业新市场格局

11月24日-25日&#xff0c;2022中国母婴业品牌大会暨第七届中国母婴产业高峰论坛在湖南省长沙市盛大召开&#xff0c;来自全国的月子中心、产后修复中心、产康中心、托育机构等近600名业内精英参与了此次盛会。由龙翔集团作为标准编制组长单位&#xff0c;作为整店输出加盟集团…

Arduino - Debugging on the Arduino IDE 2.0

文章目录Arduino - Debugging on the Arduino IDE 2.0概述如果没有官方文档, 如何手工测试哪种开发板能在Arduino IDE 中单步调试?可以调试的板子的调试按钮状态单步调试前下断点ENDArduino - Debugging on the Arduino IDE 2.0 概述 弄了2块开发板(Mega2560(MCU is ATmega2…

浏览器http缓存机制

1、前言 前端缓存主要是分为HTTP缓存和浏览器缓存。其中HTTP缓存是在HTTP请求传输时用到的缓存&#xff0c;主要在服务器代码上设置;而浏览器缓存则主要由前端开发在前端js上进行设置。 http缓存是web缓存的核心&#xff0c;是最难懂的那一部分,也是最重要的那一部分。 2、H…

【大数据clickhouse】clickhouse 数据备份与恢复

一、前言 与其他的数据存储引擎类似&#xff0c;clickhouse承载着大数据量级的数据存储&#xff0c;对于数据的备份与恢复也是必须考虑的&#xff0c;本文将通过操作演示下如何对clickhouse数据进行备份与恢复。 官网说明&#xff1a;官网备份操作说明clickhouse可以通过手动进…