多线程共享数据结构的无锁释放

news2024/11/17 3:46:42

目录

  • 背景
  • 问题
  • 共享结构的无锁释放
    • 对比
    • ref-cnt
    • rcu
    • epoch-based reclam
    • hazard pointer: 冒险指针
      • 结构
      • 原理
      • 正确性保证
      • 范例
  • 参考

背景

多线程共享一个数据结构。
共享数据结构,可以做到节约内存。
但是多线程共享,可能会有问题,比如同步的问题。

问题

在并发编程中,当我们在操作共享的内存对象时,需要考虑到其他线程是否有可能也正在访问同一对象,如果要释放该内存对象时不考虑这个问题,会引发严重的后果(访问悬空指针)。

  • 共享内存对象的回收问题
    lock-free的方法来解决共享内存对象的回收问题。

  • ABA问题
    ABA问题和内存回收问题是相关的,都是内存的生命周期管理问题。

内存回收问题解的是共享内存什么时候能安全的回收,而ABA问题解的是共享内存什么时候能被安全的重新使用。

共享结构的无锁释放

对比

在这里插入图片描述

ref-cnt

rcu

epoch-based reclam

hazard pointer: 冒险指针

总结起来,当对象正在使用时,就不能回收内存。每一个“正在使用”都需要对应一个标记。引用计数使用的标记是计数数值一,对应的原子操作性能问题就成为它无法摆脱的原罪。而 Hazard Pointer 使用的标记更为轻巧,一般通过在链表中标记该对象的指针实现,回收时如果发现链表中有对应的指针就不进行内存回收,将标记的复杂度转移到回收部分,也就更适合读多写少的场景。

结构

在这里插入图片描述

  • 数据结构
    需要为每个线程准备一些线程局部的内存,用来存储两部分内容:
    pointers: 用来存储这个线程当前正在访问的内存对象,正在访问的内存对象不能被任何线程释放;
    retire list(退休列表): 有待被这个线程释放的内存对象,但还没有释放;

  • 原理
    每个线程都将自己正在访问且不希望被任何线程释放的内存对象存放在线程局部的pointers中;
    当任何线程删除内存对象后,都需要先把该内存对象放入自己线程局部的retire list,但是不释放;
    当retire list中的内存对象数量超过一定限度时,扫描retire list,找到没有被任何线程使用的内存节点并将其安全的释放;

  • 要点
    pointers是单写多读,而retire list是单写单读的;这个性质很重要,不然的话我们又需要另一种机制来保护hazard pointer了…

原理

每个线程在申请读取某个共享的指针对象时,将指针记录下来(通常在一个per-thread 的 list「对象链表」上),读取结束时将清除该记录;而发生更新时,将更新替换下来的旧指针加入退休列表里,退休列表积攒到一定程度时则检查哪些对象已经不在其他线程的对象链表中,不再使用的则可以执行删除。

Hazard Pointer 的高性能依赖于平台上线程本地存储(TLS: thread local strore)的性能。单纯使用 CAS 更新全局的对象链表和退休列表的性能太低,可以使用 TLS 做为缓冲层,这样大部分时间都是更新本线程的数据。

正确性保证

  • 考虑这种情况:

1》线程A开始访问内存对象o,拿到了o的地址
2》线程B将内存对象o从数据结构中删除,加入retire list,并扫描所有线程的pointers,此时线程A还没有来得及将o放入到pointers中,因此3》B可以将o释放
4》线程A将o放入pointers中
5》线程A访问o,crash…

  • 分析
    看上去很危险,但实际上这种情况在hazard pointer中并不会发生,因为hazard pointer的正确性证明要求线程在pointers中持有的内存对象都必须是“连续安全的”。
    简单来说,就是说从线程取到该内存对象的一刻开始到将其放入pointers的这段过程中这个内存对象不能被其他线程从数据结构中删除,也就是说其安全状态必须是“连续”的(安全指不会被释放,在数据结构中或是加入到pointers中的的内存对象都是安全的)。

于是流程变成了这样:
1>线程A开始访问内存对象o,拿到了o的地址
2>线程B将内存对象o从数据结构中删除,加入retire list,并扫描所有线程的pointers,此时线程A还没有来得及将o放入到pointers中,因此B可以将o释放
3>线程A将o放入pointers中
4>线程A对o执行double check,发现o已经不在数据结构中了(被删除),因此认为加入到pointers中的地址是无效的,撤销并失败退出

  • 如何做到
    double check,代码如下:

范例

参考

http://blog.kongfy.com/2017/02/hazard-pointer/

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

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

相关文章

(Android-RTC-9)PeerConnectionFactory

开篇前瞎扯。很久没发技术文章了,此文一直放着草稿箱没有完成,感觉自己在家庭和工作中找到了拖延的借口,开始慢慢变得懒惰了,那是万万不行的。恰逢2023开年ChatGPT的爆火,更让我这些普通程序员危机感瞬间飙升&#xff…

安全规约第一章

文章目录传统密码和现代密码的区别古典密码近代密码现代密码定义模型证明现代密码学CryptographyCryptanalysisCryptosystemScheme如何定义算法步骤第一步,搞清楚安全服务目标第二步,计算过程中需要几方的参与需要几个算法描述它算法命名谁来运行哪一个算…

03_Apache Pulsar的Local与分布式集群构建、Pulsar的分布式集群模式、Pulsar的分布式集群模式构建\启动\测试

1.3.Apache Pulsar的Local与分布式集群构建 1.3.1 Apache Pulsar的Local模式构建 1.3.1.1.Apache Pulsar的Local模式基本使用 1.3.2.Apache Pulsar的分布式集群模式 1.3.2.1.安装zookeeper集群 1.3.3.Apache Pulsar的分布式集群模式构建 1.3.4.Apache Pulsar的分布式集群模式启…

Sparx Systems Pro Cloud Server crack

Sparx Systems Pro Cloud Server crack 云服务器 添加了新的“OSLC会话超时”设置,以配置OSLC用户将注销的最长非活动时间。 改进了对重复SQL列名的处理。 FLS:为“组”添加了对其他Microsoft Active Directory名称格式的支持。 云配置客户端 在扩展服务…

香橙派5使用NPU加速yolov5的实时视频推理(二)

三、将best.onnx转为RKNN格式 这一步就需要我们进入到Ubuntu20.04系统中了,我的Ubuntu系统中已经下载好了anaconda,使用anaconda的好处就是可以方便的安装一些库,而且还可以利用conda来配置虚拟环境,做到环境与环境之间相互独立。…

SpringCloud Alibaba入门

作为微服务刚入门的小白,不足之处请多多指教 1. Cloud Alibaba简介2.Nacos简介和下载3.Nacos安装4.Nacos之服务提供者注册5.Nacos之服务消费者注册和负载6.Nacos服务注册中心对比提升7.Nacos之服务配置中心8.Nacos之命名空间分组和DataID三者关系9.Nacos之DataID配…

如何写好单测

1、为什么要写单测? 单测即单元测试(Unit Test),是对软件的基本组成单元进行的测试,比如函数、过程或者类的方法。其意义是: 功能自测,发现功能缺陷自我Code Review测试驱动开发促进代码重构并…

File、递归、IO流(一)、IO流(二)

目录 ​File类概述 File类的常用API 判断文件类型、获取文件信息 创建文件、删除文件功能 遍历文件夹 方法递归 递归的形式和特点 递归的算法流程、核心要素 递归常见案例 递归的经典问题 非规律化递归案例-文件搜索 非规律化递归案例-啤酒问题 字符集 常见字符集…

美团二面经历——如何设计一个百万人抽奖系统?

文章目录 导图V0——单体架构V1——负载均衡V2——服务限流防止用户重复抽奖拦截无效流量服务降级和服务熔断V3 同步状态V4线程优化V5业务逻辑V6流量削峰通用思路单一职责URL动态加密静态资源——CDN服务限流数据预热削峰填谷导图 导图按照由浅入深的方式进行讲解,架构从来不是…

西电计算机组成原理(计组)核心考点汇总(期末真题+核心考点)

文章目录前言一、真题概览1.1 计组1历年真题1.2 计组2历年真题二、知识点说明2.1 计组12.1.1 冯诺依曼计算机组成和特点2.1.2 复杂指令系统计算机和特点2.1.3 精简指令系统计算机的特点2.1.4 指令长度的影响因素2.1.5 控制器2.1.6 微指令特性2.2 计组22.2.1 SMP特点与优点2.2.2…

QML动态对象管理

QML中有多种方式来动态创建和管理QML对象: Loader (加载器)Repeater(复制器)ListView,GridWiew,PethView(视图) (之后会介绍)使用加载器&#xff…

剖析G1 垃圾回收器

简单回顾 在Java当中,程序员在编写代码的时候只需要创建对象,从来不需要考虑将对象进行释放,这是因为Java中对象的垃圾回收全部由JVM替你完成了(所有的岁月静好都不过是有人替你负重前行)。 而JVM的垃圾回收由垃圾回收器来负责,在…

刷题记录:牛客NC200179Colorful Tree 奇奇怪怪的dfs序

传送门:牛客 题目描述: A tree structure with some colors associated with its vertices and a sequence of commands on it are given. A command is either an update operation or a query on the tree. Each of the update operations changes the color of a specifi…

论文阅读 - End-to-End Wireframe Parsing

文章目录1 概述2 L-CNN2.1 整体架构2.2 backbone2.3 juction proposal module2.4 line sample module2.5 line verificatoin module3 评价指标参考资料1 概述 本文是ICCV2019的一篇论文,核心是提出了一种简单的end-to-end的two-stage的检测图像中线段的方法。同时&…

192、【动态规划】leetcode ——64. 最小路径和:回溯法+动态规划(C++版本)

题目描述 原题链接&#xff1a;64. 最小路径和 解题思路 &#xff08;1&#xff09;回溯法 分别向右或下进行探查 class Solution { public:int res INT_MAX;void backtracking(vector<vector<int>>& grid, int x, int y, int pathSum) {// 超出边界&…

高可用 - 08 Keepalived集群中Master和Backup角色选举策略

文章目录概述实例说明“weight”值为正数“weight”值为负数总结概述 在Keepalived集群中&#xff0c;其实并没有严格意义上的主、备节点&#xff0c;虽然可以在Keepalived配置文件中设置“state”选项为“MASTER”状态&#xff0c;但是这并不意味着此节点一直就是Master角色。…

Python实现人脸识别,进行视频跟踪打码,羞羞的画面统统打上马赛克

哈喽兄弟们&#xff0c;我是轻松~ 今天我们来实现用Python自动对视频打马赛克前言准备工作代码实战效果展示最后前言 事情是这样的&#xff0c;昨天去表弟家&#xff0c;用了下他的电脑&#xff0c;不小心点到了他硬盘里隐藏的秘密&#xff0c;本来我只需要用几分钟电脑的&…

第一章初识Linux

文章目录Linux简介LInux的应用领域Linux OS和各种发行版的关系Linux和Unix的关系Linux相关环境配置图解VM和Linux的关系Linux自定义分三个区VMware网络连接的三种模式桥接模式NAT模式主机模式VMware快照功能Linux的操作方式Linux的目录结构各种Linux发行版本的常见目录注意事项…

GO进阶(5) 垃圾回收机制

一、前言 1、垃圾回收背景 编程语言通常会使用手动和自动两种方式管理内存&#xff0c;C、C 以及 Rust 等编程语言使用手动的方式管理内存&#xff0c;工程师需要主动申请或者释放内存&#xff1b;而 Python、Ruby、Java 和 Go 等语言使用自动的内存管理系统&#xff0c;一般都…

Java八股——wait、sleep与park

sleep()、wait()、park()都可以使线程进入等待状态&#xff0c;但是3种方式在使用上和功能上都有些不同。 共同点: wait()&#xff0c;wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权&#xff0c;进入阻塞状态它们都可以被打断唤醒都是native方法执行sleep…