Rc与Arc实现1vN所有权机制
Rc与Arc实现1vN所有权机制
- 观察引用计数的变化
- 一个例子
- 多线程无力的Rc< T >
- Arc
Rust所有权机制要求一个值只能有一个所有者,在大多数情况下,都没有问题,但是考虑以下情况:
- 在图数据结构中,多个边可能会拥有同一个节点,该节点直到没有边指向它时,才应该被释放清理
- 在多线程中,多个线程可能会持有同一个数据,但是你受限于 Rust 的安全机制,无法同时获取该数据的可变引用
为了解决这种问题,Rust通过引用计数的方式,允许一个数据资源在同一时刻拥有多个所有者。这种实现机制就是Rc和Arc,前者适用于单线程,后者适用于多线程。二者大部分情况下都相同。
fn main() {let s = String::from("hello, world");// s在这里被转移给alet a = Box::new(s);// 报错!此处继续尝试将 s 转移给 blet b = Box::new(s);
}
采用Rc就能解决
use std::rc::Rc;
fn main() {let a = Rc::new(String::from("hello, world"));let b = Rc::clone(&a);assert_eq!(2, Rc::strong_count(&a));assert_eq!(Rc::strong_count(&a), Rc::strong_count(&b))
}
使用 Rc::new 创建了一个新的 Rc< String > 智能指针并赋给变量 a,该指针指向底层的字符串数据。智能指针 Rc< T > 在创建时,还会将引用计数加 1,此时获取引用计数的关联函数 Rc::strong_count 返回的值将是 1。
接着,我们又使用 Rc::clone 克隆了一份智能指针 Rc< String >,并将该智能指针的引用计数增加到 2。由于 a 和 b 是同一个智能指针的两个副本,因此通过它们两个获取引用计数的结果都是 2。这里的clone是浅拷贝,仅仅复制了只能指针并增加了引用计数,没有克隆底层数据。
观察引用计数的变化
use std::rc::Rc;
fn main() {let a = Rc::new(String::from("test ref counting"));println!("count after creating a = {}", Rc::strong_count(&a));let b = Rc::clone(&a);println!("count after creating b = {}", Rc::strong_count(&a));{let c = Rc::clone(&a);println!("count after creating c = {}", Rc::strong_count(&c));}println!("count after c goes out of scope = {}", Rc::strong_count(&a));count after creating a = 1
count after creating b = 2
count after creating c = 3
count after c goes out of scope = 2
}
有几点值得注意:
- 由于变量 c 在语句块内部声明,当离开语句块时它会因为超出作用域而被释放,所以引用计数会减少 1,事实上这个得益于 Rc 实现了 Drop 特征
- a、b、c 三个智能指针引用计数都是同样的,并且共享底层的数据,因此打印计数时用哪个都行
- 无法看到的是:当 a、b 超出作用域后,引用计数会变成 0,最终智能指针和它指向的底层字符串都会被清理释放
事实上,RC< T >是指向底层数据的不可变引用,因此你无法通过它来修改数据,因为Rust的借用规则:要么存在多个不可变借用,要么只存在一个可变借用。
一个例子
use std::rc::Rc;struct Owner {name: String,// ...其它字段
}struct Gadget {id: i32,owner: Rc<Owner>,// ...其它字段
}fn main() {// 创建一个基于引用计数的 `Owner`.let gadget_owner: Rc<Owner> = Rc::new(Owner {name: "Gadget Man".to_string(),});// 创建两个不同的工具,它们属于同一个主人let gadget1 = Gadget {id: 1,owner: Rc::clone(&gadget_owner),};let gadget2 = Gadget {id: 2,owner: Rc::clone(&gadget_owner),};// 释放掉第一个 `Rc<Owner>`drop(gadget_owner);// 尽管在上面我们释放了 gadget_owner,但是依然可以在这里使用 owner 的信息// 原因是在 drop 之前,存在三个指向 Gadget Man 的智能指针引用,上面仅仅// drop 掉其中一个智能指针引用,而不是 drop 掉 owner 数据,外面还有两个// 引用指向底层的 owner 数据,引用计数尚未清零// 因此 owner 数据依然可以被使用println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name);println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name);// 在函数最后,`gadget1` 和 `gadget2` 也被释放,最终引用计数归零,随后底层// 数据也被清理释放
}
Rc简单总结
- Rc/Arc 是不可变引用,你无法修改它指向的值,只能进行读取,如果要修改,需要配合后面章节的内部可变性 RefCell 或互斥锁 Mutex
- 一旦最后一个拥有者消失,则资源会自动被回收,这个生命周期是在编译期就确定下来的
- Rc 只能用于同一线程内部,想要用于线程之间的对象共享,你需要使用 Arc
- Rc< T > 是一个智能指针,实现了 Deref 特征,因此你无需先解开 Rc 指针,再使用里面的 T,而是可以直接使用 T,例如上例中的 gadget1.owner.name
多线程无力的Rc< T >
use std::rc::Rc;
use std::thread;fn main() {let s = Rc::new(String::from("多线程漫游者"));for _ in 0..10 {let s = Rc::clone(&s);let handle = thread::spawn(move || {println!("{}", s)});}
}error[E0277]: `Rc<String>` cannot be sent between threads safely
表面原因是 Rc< T > 不能在线程间安全的传递,实际上是因为它没有实现 Send 特征,而该特征是恰恰是多线程间传递数据的关键,我们会在多线程章节中进行讲解。
当然,还有更深层的原因:由于 Rc< T > 需要管理引用计数,但是该计数器并没有使用任何并发原语,因此无法实现原子化的计数操作,最终会导致计数错误。
好在天无绝人之路, Rust 为我们提供的功能类似但是多线程安全的 Arc。
Arc
Arc 是 Atomic Rc 的缩写,顾名思义:原子化的 Rc 智能指针。原子化是一种并发原语,我们在后续章节会进行深入讲解,这里你只要知道它能保证我们的数据能够安全的在线程间共享即可。
为何不直接使用 Arc,还要画蛇添足弄一个 Rc,还有 Rust 的基本数据类型、标准库数据类型为什么不自动实现原子化操作?这样就不存在线程不安全的问题了。
原因在于原子化或者其它锁虽然可以带来的线程安全,但是都会伴随着性能损耗,而且这种性能损耗还不小。因此 Rust 把这种选择权交给你,毕竟需要线程安全的代码其实占比并不高,大部分时候我们开发的程序都在一个线程内。
Arc 和 Rc 拥有完全一样的 API,修改起来很简单:
use std::sync::Arc;
use std::thread;fn main() {let s = Arc::new(String::from("多线程漫游者"));for _ in 0..10 {let s = Arc::clone(&s);let handle = thread::spawn(move || {println!("{}", s)});}
}
Arc 和 Rc 并没有定义在同一个模块,前者通过 use std::sync::Arc 来引入,后者通过 use std::rc::Rc
这两者都是只读的,如果想要实现内部数据可修改,必须配合内部可变性 RefCell 或者互斥锁 Mutex 来一起使用。
相关文章:
Rc与Arc实现1vN所有权机制
Rc与Arc实现1vN所有权机制 观察引用计数的变化一个例子多线程无力的Rc< T >Arc Rust所有权机制要求一个值只能有一个所有者,在大多数情况下,都没有问题,但是考虑以下情况: 在图数据结构中,多个边可能会拥有同一个…...
建造者模式 rust和java的实现
文章目录 建造者模式介绍优点缺点使用场景 实现javarust rust代码仓库 建造者模式 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。 一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。 介绍…...
书写Prompt的经验总结
首先最重要的一点是Prompt无法全部模型都通用,可能你的Prompt在ChatGPT中使用很好,迁移到ChatGLM就不行了。不知道未来是否会出现Prompt的跨平台。 首先书写Prompt要明确告诉模型要做什么,而不是告诉它不要做什么。还要保证精简,…...
WebSocket实时应用
在开发一些前端页面的时候,总是能接收到这样的需求:如何保持页面并实现自动更新数据呢?以往的常规做法,是前端使用定时轮询后端接口,获取响应后重新渲染前端页面,这种做法虽然能达到类似的效果,…...
从零开始搭建React+TypeScript+webpack开发环境-基于lerna的webpack项目工程化改造
项目背景 在实际项目中,我们的前端项目往往是一个大型的Webpack项目,结构较为复杂。项目根目录下包含了各种配置文件、源代码、以及静态资源,整体布局相对扁平。Webpack的配置文件分散在不同的部分,包括入口文件、输出目录、加载…...
网络监控系统和防火墙的区别有哪些?
现如今,市面上保护企业网络安全的设备有很多,其中使用最多的当属网络监控系统和防火墙。 网络监控系统就是通过网页内容的自动采集处理、敏感词过滤、智能聚类分类、主题检测、专题聚焦、统计分析等多个环节,实现相关网络舆情监督管理的需要…...
刷题学习记录BUUCTF
[极客大挑战 2019]RCE ME1 进入环境直接就有代码 <?php error_reporting(0); if(isset($_GET[code])){$code$_GET[code];if(strlen($code)>40){die("This is too Long.");}if(preg_match("/[A-Za-z0-9]/",$code)){die("NO.");}eval($co…...
Linux imu6ull驱动- led
一、GPIO模块结构 开始来啃手册了,打开我们的imx6ull手册。本章我们编写的是GPIO的,打开手册的第28章,这一章就有关于IMX6ULL 的 GPIO 模块结构。 mx6ull一共有5 组 GPIO(GPIO1~GPIO5) GPIO1 有 32 个引脚&…...
【 云原生 | K8S 】Kubernetes 概述
Kubernetes 概述 1 K8S 是什么? K8S 的全称为 Kubernetes (K12345678S),PS:“嘛,写全称也太累了吧,不如整个缩写”。 作用: 用于自动部署、扩展和管理“容器化(containerized)应用…...
边缘计算多角色智能计量插座:用电监测和资产管理的未来智能化引擎
目前主流的智能插座涵盖了红外遥控(控制空调和电视等带有红外标准的电器),配备着测温、测湿等仓库应用场景,配备了人体红外或者毫米波雷达作为联动控制,但是大家有没有思考一个问题,就是随着对接的深入&…...
mysql隐式转换转换引起的bug
生产环境中遇到一个情况情况 ,过滤数据发现过滤不掉相关值情况,具体情况如下 原始数据: CREATE TABLE test (id bigint(11) NOT NULL AUTO_INCREMENT COMMENT 自增id,subject_id bigint(11) NOT NULL DEFAULT 0 COMMENT 主题id,subject_nam…...
【Python】gevent模块实现协程模拟高并发
Python中GIL的存在,导致多线程一直不是很好用,相形之下,协程的优势就更加突出了。 Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。 gevent是第三方库,通过gr…...
leetcode:2485. 找出中枢整数(python3解法)
难度:简单 给你一个正整数 n ,找出满足下述条件的 中枢整数 x : 1 和 x 之间的所有元素之和等于 x 和 n 之间所有元素之和。 返回中枢整数 x 。如果不存在中枢整数,则返回 -1 。题目保证对于给定的输入,至多存在一个中…...
asp.net core mvc之模型绑定、特性约束模型绑定、模型验证(服务器/客户端/远程)
一、不用模型绑定 数据类型都是string 1、UserController.cs public class UserController : Controller {public IActionResult Register(){return View();}[HttpPost]public IActionResult DoRegister(){//不用模型绑定 以前的方法取表单数据或Url的参数//数据类型都是s…...
AI诈骗防范:保护数字社会的安全前线
引言: 在当今数字化的时代,人工智能技术在各个领域都展现了巨大的潜力,然而,正是这些技术的广泛应用也催生了新的安全隐患。近期,AIGC在聊天、写作、绘画、编程等领域表现出色,但也被用于“AI换脸”、“AI换…...
(待完善)python学习参考手册
这里写目录标题 观前浅谈:学习路线 :学习心得笔记:Step1:简单但一问不知怎么的组织语言去回答的小问题:什么是提示符?python解释器是什么?请正在阅读本文的朋友,安装一下PyCharm以及如何进行科学的省钱:Python中的命令行模式和交互模式的区别是什么?请正在阅读本文的朋友安装…...
STM32-HAL库09-CAN通讯(loopback模式)
一、所用材料: STM32F103C6T6最小系统板 STM32CUBEMX(HAL库软件) MDK5 串口调试助手 二、所学内容: 初步学习如何使用STM32的CAN通讯功能,在本章节主要达到板内CAN通讯的效果,即32发送CAN信息再在CAN接收…...
jsvascript使用dhtmlXTreeObject的loadJSONObject绘制目录树
文章目录 1,引入dhtmlXTreeObject的css和js文件2,创建一棵目录树2.1,let tree new dhtmlXTreeObject(id-dhtmltree-0, "100%", "100%", 0);2.2,设置图片根目录(后续使用到的图片都是相对于该目录…...
LeetCode 17. 电话号码的字母组合 中等
题目 - 点击直达 1. 17. 电话号码的字母组合 中等1. 题目详情1. 原题链接2. 题目要求3. 基础框架 2. 解题思路1. 思路分析2. 时间复杂度3. 代码实现 3. 知识与收获 1. 17. 电话号码的字母组合 中等 1. 题目详情 1. 原题链接 LeetCode 17. 电话号码的字母组合 中等 2. 题目要…...
《GPT与AI助手:技术进步与就业前景》
随着人工智能的迅速发展,像GPT(Generative Pre-trained Transformer)这样的自然语言处理技术已经广泛应用于各个领域,各个互联网公司也纷纷推出了自己的AI助手来帮助创作、交流和解决问题。这一技术的广泛应用引发了一系列关于就业…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
