C++ 加锁的原则

2023-11-06

规则1 多线程、进程并行访问共享资源时,一定要加锁保护
说明:共享资源包括全局变量,静态变量,共享内存,文件等。 建议封装像智能指针一样的对象对锁进行管理,比如我们就封装了一个auto_lock,在构造时申请 锁,析构中释放锁,保证不会忘记“解锁”。如果锁的作用范围有限,则可以这样: do { auto_lock lock(&lock); //… }while(0);

规则2 锁的职责单一
说明:每个锁只锁一个唯一共享资源;这样,才能保证锁应用的单一,也能更好的确保加锁的范围尽 量小。 对于共享全局资源,应该根据实际需要,每类或每个资源,有一把锁。这样,这把锁只锁对这个资源 访问的代码,通常这样的代码都会是比较简单的资源操作代码,不会是复杂的函数调用等。相反,如 果我们对几类或几个资源共用一把锁。这把锁的责任范围就大了,使用复杂,很难理清锁之间的关系(有 没有释放锁,或者锁之间的嵌套加锁等),容易导致死锁问题。

规则 3 锁范围尽量小,只锁对应资源操作代码
说明:使用锁时,尽量减少锁的使用范围。我们使用锁,为了方便,会大范围的加锁,如:直接锁几 个函数调用。这种使用,一方面会导致多线程执行效率的低下,容易变成串行执行;另一方面,容易 出现锁未释放,或者锁的代码中再加锁的场景,最后导致死锁。 所以,对锁操作的最好办法,就是只锁简单资源操作代码。对应资源访问完后,马上释放锁。尽量在 函数内部靠近资源操作的地方加锁而不是靠近线程、函数外部加锁。

规则4 避免嵌套加锁;如果必须加锁,务必保证不同地方的加锁顺序是一样的
说明:加上一把锁之后,在释放之前,不能再加锁。 典型的锁中加锁的场景:OMU代码中对几个容器的同时遍历,每个容器一把锁,就导致需要加多把锁。 这种场景的解决方法:先加一把锁,对一个容器遍历,选择出合乎要求的数据,并保存在临时变量中; 再加另一把锁,使用临时变量,再对其他容器遍历。 锁中加锁,必须保证加锁的顺序是一样的,比如先加的锁后解锁, Lock1 Lock2 Unlock2 Unlock1 则其他地方的加锁顺序,必须与这里的顺序一样,避免死锁,不允许出现: lock2 lock1 unlock2 unlock1

建议1 进程间通讯,使用自己保证互斥的数据库系统、共享内存,或socket消息机制;尽量避免使用文件等进程无法管理的资源
说明:由于文件在不同进程间访问,无法保证互斥。当然,可以在进程间加进程锁,但只受限于我们能加锁的进程,对于第三方进程等无法保证。这样,当多个进程同时对文件进行写操作时,将会导致文件数据破坏,或文件写失败等问题。
当数据库系统本身的访问接口带有互斥机制,当多个进程同时访问时,可以保证数据库数据的完整。
共享内存,只限制于使用共享内存的几个进程,需要我们对这些访问共享内存的进程加锁。但由于共享内存,第三方进程等无法访问,这也能比较好的保护数据,避免文件系统存在的问题。
socket消息机制,由操作系统socket通讯机制保证互斥,在多个进程间,通过消息来保证数据的互斥。 进程的消息都是操作系统转发而来的独立数据,属于进程私有数据,不存在进程间并行访问的问题。

建议2 可重入函数尽量只使用局部变量和函数参数,少用全局变量、静态变量
说明:支持多线程并行访问的函数称之为可重入函数。设计可重入函数时,尽量使用局部变量和函数 参数来传递数据,在多线程并行访问时,互相之间不会受影响。相反,如果使用全局变量、静态变量, 就需要同步。 示例: int iTotalCnt = 10; void WriteFile() { for (int i=0; i<iTotalCnt; i++) { //写个数据到内存; } iTotalCnt = 0; //写完数据之后,赋值计数为0 } 上面的函数,如果是并行访问,将会导致有部分调用WriteFile的线程,不执行for循环;因为iTotalCnt 可能被其他线程修改为0。 引申:一些库函数也是非线程安全,调用时可能会出现多线程并发访问问题。

建议3 锁中避免调用函数;如果必须调用函数,务必保证不会造成死锁
说明:这条规则是对加锁范围尽量小(只锁对应资源操作代码)规则的补充。不能把调用函数也加到加 锁范围中。因为被调用函数的内部到底做了什么事情,是如何做的,调用者可能不是很清楚。尤其是 当被调用函数内部又加锁的情况,就容易导致两个锁互饿,导致死锁。 示例: Callfunc() { Lock2; //…. Unlock2; } Thread_func1() { Lock1; Callfunc(); Unlock1; } Thread_func2() { Lock2; Lock1; //… Unlock1; Unlock2; } 当上述线程函数Thread_func1()和Thread_func2()并行执行时,就很有可能导致死锁。而且这种死锁 情况还是比较难分析。因为我们调用函数,很多时候只关注函数实现的功能 ,而忽略函数内部的具体实现。 其次,锁中调用函数,也会把对资源操作的代码扩大化,不利于并行效率。更主要的是,这种操作, 由于加锁的范围变大,引起死锁的可能就增大。

建议4 锁中避免使用跳转语句
说明:跳转语句包含return、break、continue、goto等。如果锁中有宏调用的代码,要特别注意,分 析宏中是否存在隐含的跳转语句。 在函数返回时忘记把锁释放,特别是存在很多分支都可能返回的时候,可能一些分支会忘记释放锁。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 加锁的原则 的相关文章

随机推荐

  • AutoSAR 学习笔记2:AutoSAR架构

    1 应用层 ASW 2 运行时环境层 RTE RTE 是专门为应用软件 AutoSAR 软件组件和 或 AutoSAR 传感器 执行器组件 提供通信服务的层 在 RTE 之上 软件架构风格从 分层 转变为 组件风格 AutoSAR 软件组件
  • 模板模式

    1 模板模式的概念 在模板模式 Template Pattern 中 一个抽象类公开定义了执行它的方法的方式 模板 它的子类可以按需要重写方法实现 但调用将以抽象类中定义的方式进行 这种类型的设计模式属于行为型模式 2 模板模式的特点 子类
  • 从煎鸡蛋的角度理解编程的思维和流程,你适合学吗?

    其实很多门外人对编程都是懵懵懂懂的 我们可以先看一张图来理解一下 思维 就是程序员需要考虑到的各种需求 也就是我们想让计算机帮助我们实现什么 表达 就是计算机可以看懂的指令也就是0和1 那怎么将我们所想向计算机说出来 并且让它帮我们执行 就
  • server2008r2域控时间设置internet时间同步(备忘)

    windows server 2008 r2成为域控后 时间设置里的 internet时间就没有了 为了解决这个问题 用以下CMD命令可解决 w32tm config manualpeerlist time windows com sync
  • iOS 应用获取最上层全屏 Window 的正确方法

    有时候 我们需要将View添加到最上层的Window上 比如 弹出框 Loading等 经常有同学直接通过 UIApplication sharedApplication windows lastObject 来获取 这种方法是非常不严谨的
  • leetcode----JavaScript 详情题解(4)

    目录 2722 根据 ID 合并两个数组 2723 添加两个 Promise 对象 2724 排序方式 2725 间隔取消 2726 使用方法链的计算器 2727 判断对象是否为空 2624 蜗牛排序 2694 事件发射器 2722 根据
  • 集成支付宝报错订单信息有错误,建议联系实家。 错误码: TOTAL FEE EXCEED

    问题 集成支付宝报错 订单信息有错误 建议联系实家 错误码 TOTAL FEE EXCEED 详细问题 笔者按照支付宝沙箱支付快速集成版进行操作 操作完成访问所集成的支付宝 页面如下 发起请求核心代码 response sendRedire
  • ubuntu(20.04)-shell脚本(4)-vmstat-iostat-expr-netstat-arp-Tracert-Route-NBTStat

    vmstat 好iostat 两个命令都适用于所有主要的类unix系统 linux的软件包 都在sysstat软件包中 1 vmstat iostat 基本语法 每列的意义 常用的 Free 空闲的内存空间 si 每秒从磁盘中交换进内存的数
  • 项目问题总结

    1 android studio 导入开源项目源码时要注意与自己包的冲突 比如 你有一个com xxxx的包 而需要导入的是com xx yy 你就不能把整个包复制过来 否则会报can t resolve symbil 因为它根据com会到
  • 虚幻4常见问题

    问题1 问题描述 UE4找不到游戏模块 UE4 the game module fps could not be found 解决方案 重新编译一遍C 项目 通过C 项目启动UE4生成游戏模块 为了防止生成失效可以启动uproject文件再
  • 数组是分配在栈中的

    关于JAVA堆 下面说法错误的是 正确答案 C 你的答案 B 错误 所有类的实例和数组都是在堆上分配内存的 堆内存由存活和死亡的对象 空闲碎片区组成 数组是分配在栈中的 对象所占的堆内存是由自动内存管理系统回收 JVM 关于堆和栈 Java
  • Java 数据转换/进制转换 工具类

    public class ByteUtil 十六进制转为十进制 public static String getHexToTen String hex return String valueOf Integer parseInt hex 1
  • Contact Form 7 获取用户IP和留言url,发布时间

    提交询盘时间 date time 客户访问IP a href remote ip a 点击打开可看到ip所属国家 客户访问产品 url 客户访问日期 date 客户访问者有没有facebook a href your email a
  • XXE(外部实体注入)

    写在前面 这个系列开始写写XXE相关的东西 这里是第一部分 相关资料及使用靶场如下 XML学习 靶场链接 XXE是以XML为基础进行的一种攻击 所以你需要先学习XML 为了更方便你检索题目且由于是国外网站 会带有一定外语及翻译 最后 如果你
  • 监听pda扫描_android系统PDA扫描枪,扫描完成后自带回车,为什么回车监听第一次不起作用,手动提交一次后才能正常提交...

    如题 第一次扫描后 在条码后出现的是回车 而不是绑定的提交按钮的提交功能 手动软键盘提交后 再回到扫描页 再次扫描 就会自动执行提交功能 下面附上源码 privateImageButton 如题 第一次扫描后 在条码后出现的是回车 而不是绑
  • openGL 调用glewInit()失败

    openGL系列文章目录 文章目录 openGL系列文章目录 前言 一 glew官网 二 glew库初始化调用失败 1 引入库 2 glew调用失败原因 着色器 运行结果 前言 OpenGL Extension Wrangler Libra
  • 垂类模型大有前景,但AGI却给自己“挖了个坑”

    巨量模型是个 坑 但垂直模型不是 数科星球原创 作者丨苑晶 编辑丨大兔 2023年4月 GPT 5的相关消息引起了一阵轰动 彼时 人们对巨量大模型既有期待 也有恐惧 更有甚者 认为人类历史或许将因此而画上终止符 但很快 从业者便发现 巨量大
  • ZYNQ学习之路(三):自定义IP实现PL处理PS写入BRAM的数据

    目录 一 实验简介 二 vivado部分处理 三 SDK编程 四 实验测试 五 总结 一 实验简介 ZYNQ系列嵌入式FPGA可以使PS将数据写入PL部分BRAM PL可以将数据读取后再重新写入BRAM PS将数据读出后再传走 这样可以使P
  • 数据结构--一个数组实现两个栈

    用一个数组实现两个栈 通常我们会想到以下几种方案 1 奇偶栈 即就是将数组的偶数位置看作一个栈的存储空间 将奇数位置看作另一个栈的存储空间 2 从中间分别向两边展开 即就是将数组的中间位置看作是两个栈的栈底 压栈时栈顶指针分别向两边移动 当
  • C++ 加锁的原则

    规则1 多线程 进程并行访问共享资源时 一定要加锁保护 说明 共享资源包括全局变量 静态变量 共享内存 文件等 建议封装像智能指针一样的对象对锁进行管理 比如我们就封装了一个auto lock 在构造时申请 锁 析构中释放锁 保证不会忘记