【译】Arc 在 Rust 中是如何工作的
原文标题:How Arc works in Rust
原文链接:https://medium.com/@DylanKerler1/how-arc-works-in-rust-b06192acd0a6
公众号: Rust 碎碎念
翻译 by: Praying
原子引用计数(Arc)类型是一种智能指针,它能够让你以线程安全的方式在线程间共享不可变数据。我还没有发现能够很好地解释它的工作原理的文章,所以我决定尝试来写一篇。(文章)第一部分是介绍怎样使用Arc和为什么要使用Arc;如果你已经了解这部分内容,只是想知道它是如何工作的,可以直接跳到第二部分:“它是怎样工作的(How does it work)”。
为什么你需要使用Arc?
当你试图在线程间共享数据时,需要Arc类型来保证被共享的类型的生命周期,与运行时间最长的线程活得一样久。考虑下面的例子:
use std::thread;
use std::time::Duration;
fn main() {
let foo = vec![0]; // creation of foo here
thread::spawn(|| {
thread::sleep(Duration::from_millis(20));
println!("{:?}", &foo);
});
} // foo gets dropped here
// wait 20 milliseconds
// try to print foo
这段代码无法编译通过。我们会得到一个错误,称foo的引用活得比foo自身更久。这是因为foo在main函数结尾处就被丢弃(drop)了,并且这个被丢弃的值会在20毫秒后在生成的线程中被试图访问。这就是Arc的作用所在。原子引用计数确保在对foo类型的所有引用都结束之前,它不会被丢弃——因此即使在main函数结束之后,foo仍然会存在。现在考虑下面的示例:
use std::thread;
use std::sync::Arc;
use std::time::Duration;
fn main() {
let foo = Arc::new(vec![0]);
let bar = Arc::clone(&foo);
thread::spawn(move || {
thread::sleep(Duration::from_millis(20));
println!("{:?}", *bar);
});
println!("{:?}", foo);
}
在这个例子中,我们可以在(主)线程中引用foo并且还可以在(子)线程被生成之后访问它的值。
它是怎样工作的?
你已经知道如何使用Arc了,现在让我们讨论一下它是如何工作的。当你调用let foo = Arc::new(vec![0])时,你同时创建了一个vec![0]和一个值为1的原子引用计数,并且把它们都存储在堆上的相同位置(紧挨着)。指向堆上的这份数据的指针存放在foo中。因此,foo是由指向一个对象的指针构成,被指向的对象包含vec![0]和原子计数。
当你调用let bar = Arc::clone(&foo)时,你是在获取foo的一个引用、对foo(指向存放在堆上的数据的指针)解引用、接着找到foo指向的地址、找出里面存放的值(vec![0]和原子计数)、把原子计数加一、最后把指向vec![0]的指针保存在bar中。
当foo或bar离开作用域时,Arc::drop()就被调用了,原子计数减一。如果Arc::drop()发现原子计数等于0,那么它所指向的堆上的数据(vec![0]和原子计数)会被清理并从堆上擦除。
原子计数是一种能够让你以线程安全的方式修改和增加它的值的类型;在对原子类型允许进行其他操作之前,前面的原子类型操作必须要全部完成;因此被称为原子的(atomic)(即不可分割的)(操作)。
需要注意的是,Arc只能包含不可变数据。这是因为如果两个线程试图在同一时间修改被包含的值,Arc无法保证避免数据竞争。如果你希望修改数据,你应该在Arc类型内部封装一个互斥锁保护(Mutex guard)。
为什么这些东西能让Arc是线程安全的呢?
Arc是线程安全的是因为它给编译器保证数据的引用至少活得和数据本身一样长(译注:这里原作者应该是想表达,数据的引用存在期间,数据都是有效的)。这是因为每次你创建一个对堆上数据得引用,原子计数就会加一,数据只有在当原子计数等于零得时候才会被丢弃(每当一个引用离开作用域时,原子计数会减一)——Arc和一个普通得Rc(引用计数)之间得区别就在于原子计数。
那么Rc有什么用,为什么不用Arc来做所有事情?
原因是,原子计数是一个(开销)昂贵的变量类型,而普通的usize类型则没有这些开销。原子类型不仅在实际的程序中占用更多的内存,而且每个原子类型的操作还需要更长的时间,因为它必须分配资源来为对其自身进行读写的调用维护一个队列进而保证原子性。

【译】Arc 在 Rust 中是如何工作的的更多相关文章
- 【译】理解Rust中的Futures (一)
原文标题:Understanding Futures In Rust -- Part 1 原文链接:https://www.viget.com/articles/understanding-futur ...
- 【译】理解Rust中的Futures(二)
原文标题:Understanding Futures in Rust -- Part 2 原文链接:https://www.viget.com/articles/understanding-futur ...
- 【译】理解Rust中的闭包
原文标题:Understanding Closures in Rust 原文链接:https://medium.com/swlh/understanding-closures-in-rust-21f2 ...
- 【译】理解Rust中的局部移动
原文标题:Understanding Partial Moves in Rust 原文链接:https://whileydave.com/2020/11/30/understanding-partia ...
- 【译】为什么Rust中的BTreeMap没有with_capacity()方法?
原文标题:Why doesn't Rust's BTreeMap have a with_capacity() method? 原文链接:https://www.nicolas-hahn.com/20 ...
- 【译】对Rust中的std::io::Error的研究
原文标题:Study of std::io::Error 原文链接:https://matklad.github.io/2020/10/15/study-of-std-io-error.html 公众 ...
- 【译】深入理解Rust中的生命周期
原文标题:Understanding Rust Lifetimes 原文链接:https://medium.com/nearprotocol/understanding-rust-lifetimes- ...
- 【译】Rust中的array、vector和slice
原文链接:https://hashrust.com/blog/arrays-vectors-and-slices-in-rust/ 原文标题:Arrays, vectors and slices in ...
- 【译】关于Rust模块的清晰解释
原文链接: http://www.sheshbabu.com/posts/rust-module-system/ 原文标题: Clear explanation of Rust's module sy ...
随机推荐
- js、css等文件引入空白问题
路径没错,不管路径怎么改变,js.css等文件就是引入失败.很多时候是因为Spring的过滤器把js.css等资源文件拦截了.default是tomcat配置的一个servlet,"Defa ...
- redhat系统服务器重启后提示An error occurred during the file system check.
问题描述 浪潮一台NF8480M3外观红灯报警,鉴于无法登陆带外,只能对服务器进行断电重启操作 问题现象 重启后进入开机过程并报错,报错如下内容及图片如下所示,正常来说进入此界面后直接输入root密码 ...
- spring boot:thymeleaf模板中insert/include/replace三种引用fragment方式的区别(spring boot 2.3.3)
一,thymeleaf模板中insert/include/replace三种引用fragment方式的区别 insert: 把整个fragment(包括fragment的节点tag)插入到当前节点内部 ...
- 如何高效定义和验证restful请求的参数
go-zero针对文本的序列化和反序列化主要在三个地方使用 http api请求体的反序列化 http api返回体的序列化 配置文件的反序列化 完整示例可参照下面这篇文章: 快速构建高并发微服务 1 ...
- 源代码 VS 汇编代码 VS 目标代码 VS 字节码 VS 机器码
1.源代码(source code) 源代码就是平时我们开发的代码:比如C.Java.Python.Shell...等 public class HelloWorld { public static ...
- 1-1Java概述
001_Java语言发展史 Sun公司:Stanford University Network 002Java跨平台原理 平台:指的是操作系统Windows,Mac,Linux等. 总结:在需要运行 ...
- mysql优化篇(基于索引)
在上一篇文章:Mysql索引(一篇就够le) 中介绍了索引的基本使用,分类和原理,也强烈建议先读Mysql索引(一篇就够le),然后继续本文的阅读 我们也知道mysql的优化可以从很多的方面进行,比如 ...
- IDEA Cannot resolve plugin org.apache.maven.plugins:maven-site-plugin:3.8.2-plugin爆红错误
如果确认本地库存在,maven仓库配置正确,将其显式声明出来,问题解决 <!--报找不到该依赖的错误, 本地库又存在,将其显式声明在这里,问题解决--> <plugin> &l ...
- redis过期事件,swoole监听
$serv = new Swoole\Server('0.0.0.0', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set(array( 'work ...
- javascript常见面试题之一:数组的冒泡排序;
var arr=[32,2,7,78,90,10]; //外层循环控制轮数: for (var i = 0; i < arr.length; i++) { //内层循环控制次数: for (va ...