Struct std::sync::Arc 1.0.0[−][src]
pub struct Arc<T> where
T: ?Sized, { /* fields omitted */ }
Expand description
线程安全的引用计数指针。Arc
代表原子引用计数。
Arc<T>
类型提供了在堆中分配的 T
类型值的共享所有权。在 Arc
上调用 clone
会生成一个新的 Arc
实例,该实例指向堆上与源 Arc
相同的分配,同时增加了引用计数。
当指向给定分配的最后一个 Arc
指针被销毁时,存储在该分配中的值 (通常称为 “内部值”) 也将被丢弃。
默认情况下,Rust 中的共享引用不允许可变的,Arc
也不例外: 您通常无法获得 Arc
内部内容的可变引用。如果需要通过 Arc
进行可变的,请使用 Mutex
,RwLock
或 Atomic
类型之一。
线程安全
与 Rc<T>
不同,Arc<T>
对其引用计数使用原子运算。这意味着它是线程安全的。缺点是原子操作比普通的内存访问更昂贵。如果您不共享线程之间的引用计数分配,请考虑使用 Rc<T>
来降低开销。
Rc<T>
是安全的默认设置,因为编译器将捕获在线程之间发送 Rc<T>
的任何尝试。
但是,磁带库可能选择 Arc<T>
,以便为磁带库使用者提供更大的灵活性。
Arc<T>
只要 T
实现 Send
和 Sync
,就将实现 Send
和 Sync
。
为什么不能在 Arc<T>
中放置非线程安全类型 T
使其成为线程安全的? 起初这可能有点违反直觉: 毕竟,Arc<T>
线程安全性不是重点吗? 关键在于: Arc<T>
使具有同一数据的多个所有权成为线程安全的,但并未为其数据增加线程安全。
考虑 Arc<
RefCell<T>
>
。
RefCell<T>
不是 Sync
,并且如果 Arc<T>
始终是 Send
,那么 Arc<
RefCell<T>
>
也是如此。
但是然后我们会遇到一个问题:
RefCell<T>
不是线程安全的; 它使用非原子操作来跟踪借用计数。
最后,这意味着您可能需要将 Arc<T>
与某种 std::sync
类型 (通常为 Mutex<T>
) 配对。
Weak
的中断循环
downgrade
方法可用于创建非所有者 Weak
指针。Weak
指针可以 upgrade
为 Arc
,但如果存储在分配中的值已经被丢弃,这将返回 None
。
换句话说,Weak
指针不会使分配内部的值保持活动状态。然而,他们确实保持分配 (值的后备存储) 处于活动状态。
Arc
指针之间的循环将永远不会被释放。
因此,Weak
用于中断循环。例如,一棵树可能具有从父节点到子节点的强 Arc
指针,以及从子节点返回到其父节点的 Weak
指针。
克隆引用
使用为 Arc<T>
和 Weak<T>
实现的 Clone
trait 从现有的引用计数指针创建新的引用。
use std::sync::Arc; let foo = Arc::new(vec![1.0, 2.0, 3.0]); // 以下两种语法是等效的。 let a = foo.clone(); let b = Arc::clone(&foo); // a,b 和 foo 都是指向相同内存位置的弧Run
Deref
behavior
Arc<T>
自动取消对 T
的引用 (通过 Deref
trait),因此您可以在 Arc<T>
类型的值上调用 T
的方法。为了避免与 T 方法的名称冲突,Arc<T>
本身的方法是关联函数,使用 fully qualified syntax 进行调用:
use std::sync::Arc; let my_arc = Arc::new(()); Arc::downgrade(&my_arc);Run
Arc<T>
也可以使用完全限定语法来调用诸如 Clone
之类的 traits 实现。
有些人喜欢使用完全限定的语法,而另一些人则喜欢使用方法调用语法。
use std::sync::Arc; let arc = Arc::new(()); // 方法调用语法 let arc2 = arc.clone(); // 完全限定的语法 let arc3 = Arc::clone(&arc);Run
Weak<T>
不会自动解引用到 T
,因为内部值可能已被丢弃。
Examples
在线程之间共享一些不可变数据:
use std::sync::Arc; use std::thread; let five = Arc::new(5); for _ in 0..10 { let five = Arc::clone(&five); thread::spawn(move || { println!("{:?}", five); }); }Run
共享可变 AtomicUsize
:
use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; let val = Arc::new(AtomicUsize::new(5)); for _ in 0..10 { let val = Arc::clone(&val); thread::spawn(move || { let v = val.fetch_add(1, Ordering::SeqCst); println!("{:?}", v); }); }Run
有关更多一般引用计数示例,请参见 rc
文档。
Implementations
创建一个具有未初始化内容的新 Arc
,并用 0
字节填充内存。
有关正确和不正确使用此方法的示例,请参见 MaybeUninit::zeroed
。
Examples
#![feature(new_uninit)] use std::sync::Arc; let zero = Arc::<u32>::new_zeroed(); let zero = unsafe { zero.assume_init() }; assert_eq!(*zero, 0)Run
创建一个新的 Pin<Arc<T>>
。
如果 T
未实现 Unpin
,则 data
将被固定在内存中并且无法移动。
创建一个新的 Pin<Arc<T>>
,如果分配失败则返回错误。
构造具有未初始化内容的新 Arc
,如果分配失败,则返回错误。
Examples
#![feature(new_uninit, allocator_api)] #![feature(get_mut_unchecked)] use std::sync::Arc; let mut five = Arc::<u32>::try_new_uninit()?; let five = unsafe { // 延迟初始化: Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); five.assume_init() }; assert_eq!(*five, 5);Run
创建一个具有未初始化内容的新 Arc
,并用 0
字节填充内存,如果分配失败,则返回错误。
有关正确和不正确使用此方法的示例,请参见 MaybeUninit::zeroed
。
Examples
#![feature(new_uninit, allocator_api)] use std::sync::Arc; let zero = Arc::<u32>::try_new_zeroed()?; let zero = unsafe { zero.assume_init() }; assert_eq!(*zero, 0);Run
创建一个具有未初始化内容的新原子引用计数切片。
Examples
#![feature(new_uninit)] #![feature(get_mut_unchecked)] use std::sync::Arc; let mut values = Arc::<[u32]>::new_uninit_slice(3); let values = unsafe { // 延迟初始化: Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); values.assume_init() }; assert_eq!(*values, [1, 2, 3])Run
创建一个具有未初始化内容的新原子引用计数切片,内存中填充 0
字节。
有关正确和不正确使用此方法的示例,请参见 MaybeUninit::zeroed
。
Examples
#![feature(new_uninit)] use std::sync::Arc; let values = Arc::<[u32]>::new_zeroed_slice(3); let values = unsafe { values.assume_init() }; assert_eq!(*values, [0, 0, 0])Run
转换为 Arc<T>
。
Safety
与 MaybeUninit::assume_init
一样,由调用方负责确保内部值确实处于初始化状态。
在内容尚未完全初始化时调用此方法会立即导致未定义的行为。
Examples
#![feature(new_uninit)] #![feature(get_mut_unchecked)] use std::sync::Arc; let mut five = Arc::<u32>::new_uninit(); let five = unsafe { // 延迟初始化: Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); five.assume_init() }; assert_eq!(*five, 5)Run
转换为 Arc<[T]>
。
Safety
与 MaybeUninit::assume_init
一样,由调用方负责确保内部值确实处于初始化状态。
在内容尚未完全初始化时调用此方法会立即导致未定义的行为。
Examples
#![feature(new_uninit)] #![feature(get_mut_unchecked)] use std::sync::Arc; let mut values = Arc::<[u32]>::new_uninit_slice(3); let values = unsafe { // 延迟初始化: Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); values.assume_init() }; assert_eq!(*values, [1, 2, 3])Run
消耗 Arc
,返回包装的指针。
为避免内存泄漏,必须使用 Arc::from_raw
将指针转换回 Arc
。
Examples
use std::sync::Arc; let x = Arc::new("hello".to_owned()); let x_ptr = Arc::into_raw(x); assert_eq!(unsafe { &*x_ptr }, "hello");Run
从裸指针构造 Arc<T>
。
裸指针必须事先由调用返回到 Arc<U>::into_raw
,其中 U
的大小和对齐方式必须与 T
相同。
如果 U
是 T
,这是很简单的。
请注意,如果 U
不是 T
,但是具有相同的大小和对齐方式,则基本上就像对不同类型的引用进行转换一样。
有关在这种情况下适用的限制的更多信息,请参见 mem::transmute
。
from_raw
的用户必须确保 T
的特定值仅被丢弃一次。
此函数不安全,因为使用不当可能会导致内存不安全,即使从未访问返回的 Arc<T>
也是如此。
Examples
use std::sync::Arc; let x = Arc::new("hello".to_owned()); let x_ptr = Arc::into_raw(x); unsafe { // 转换回 `Arc` 以防止泄漏。 let x = Arc::from_raw(x_ptr); assert_eq!(&*x, "hello"); // 进一步调用 `Arc::from_raw(x_ptr)` 将导致内存不安全。 } // 当 `x` 离开作用域时,内存被释放,所以 `x_ptr` 现在悬空了!Run
与提供的指针关联的 Arc<T>
上的强引用计数加 1。
Safety
指针必须已经通过 Arc::into_raw
获得,并且关联的 Arc
实例必须有效 (即
在此方法的持续时间内,强引用计数必须至少为 1)。
Examples
use std::sync::Arc; let five = Arc::new(5); unsafe { let ptr = Arc::into_raw(five); Arc::increment_strong_count(ptr); // 此断言是确定性的,因为我们尚未在线程之间共享 `Arc`。 let five = Arc::from_raw(ptr); assert_eq!(2, Arc::strong_count(&five)); }Run
将与提供的指针关联的 Arc<T>
上的强引用计数减 1。
Safety
指针必须已经通过 Arc::into_raw
获得,并且关联的 Arc
实例必须有效 (即
调用此方法时,强引用计数必须至少为 1)。
此方法可用于释放最终的 Arc
和后备存储,但是在释放最终的 Arc
之后,不应 调用。
Examples
use std::sync::Arc; let five = Arc::new(5); unsafe { let ptr = Arc::into_raw(five); Arc::increment_strong_count(ptr); // 这些断言是确定性的,因为我们尚未在线程之间共享 `Arc`。 let five = Arc::from_raw(ptr); assert_eq!(2, Arc::strong_count(&five)); Arc::decrement_strong_count(ptr); assert_eq!(1, Arc::strong_count(&five)); }Run
对给定的 Arc
进行可变引用。
如果还有其他 Arc
或 Weak
指针指向同一分配,则 make_mut
将创建一个新分配,并在内部值上调用 clone
以确保唯一的所有权。
这也称为写时克隆。
请注意,这与 Rc::make_mut
的行为不同,后者取消关联所有剩余的 Weak
指针。
另请参见 get_mut
,它将失败而不是克隆。
Examples
use std::sync::Arc; let mut data = Arc::new(5); *Arc::make_mut(&mut data) += 1; // 不会克隆任何东西 let mut other_data = Arc::clone(&data); // 不会克隆内部数据 *Arc::make_mut(&mut data) += 1; // 克隆内部数据 *Arc::make_mut(&mut data) += 1; // 不会克隆任何东西 *Arc::make_mut(&mut other_data) *= 2; // 不会克隆任何东西 // 现在,`data` 和 `other_data` 指向不同的分配。 assert_eq!(*data, 8); assert_eq!(*other_data, 12);Run
将变量引用返回给定的 Arc
,而不进行任何检查。
另请参见 get_mut
,它是安全的并且进行适当的检查。
Safety
在返回的借用期间,不得解引用其他指向相同分配的 Arc
或 Weak
指针。
如果不存在这样的指针 (例如紧接在 Arc::new
之后),则情况很简单。
Examples
#![feature(get_mut_unchecked)] use std::sync::Arc; let mut x = Arc::new(String::new()); unsafe { Arc::get_mut_unchecked(&mut x).push_str("foo") } assert_eq!(*x, "foo");Run
尝试将 Arc<dyn Any + Send + Sync>
转换为具体类型。
Examples
use std::any::Any; use std::sync::Arc; fn print_if_string(value: Arc<dyn Any + Send + Sync>) { if let Ok(string) = value.downcast::<String>() { println!("String ({}): {}", string.len(), string); } } let my_string = "Hello World".to_string(); print_if_string(Arc::new(my_string)); print_if_string(Arc::new(0i8));Run
Trait Implementations
use the Display impl or to_string()
replaced by Error::source, which can support downcasting
获取 Iterator
中的每个元素,并将其收集到 Arc<[T]>
中。
性能特点
一般情况
在一般情况下,首先要收集到 Vec<T>
中来收集到 Arc<[T]>
中。也就是说,编写以下内容时:
let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0).collect();Run
这就像我们写的那样:
let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0) .collect::<Vec<_>>() // 第一组分配在此处发生。 .into(); // `Arc<[T]>` 的第二个分配在此处进行。Run
这将分配构造 Vec<T>
所需的次数,然后分配一次,以将 Vec<T>
转换为 Arc<[T]>
。
已知长度的迭代器
当您的 Iterator
实现 TrustedLen
且大小正确时,将为 Arc<[T]>
进行一次分配。例如:
let evens: Arc<[u8]> = (0..10).collect(); // 这里只进行一次分配。Run