上一篇的 Rc 不能跨线程,Arc 就是它的线程安全版本。这篇验证 Arc 能做什么、不能做什么、对 T 有什么要求,并量一量「原子计数」到底贵不贵 —— 结果是:不争用时几乎不贵,争用时非常贵。
源码:
1 | use std::sync::Arc;
2 | use std::thread;
3 | fn main() {
4 | let data = Arc::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
5 | let mut hs = vec![];
6 | for i in 0..4 {
7 | let d = Arc::clone(&data);
8 | hs.push(thread::spawn(move || { let s: i32 = d[i * 2..i * 2 + 2].iter().sum(); (i, s) }));
9 | }
10 | for h in hs { let (i, s) = h.join().unwrap(); println!("线程 {i} 求和 = {s}"); }
11 | println!("全部线程结束后 strong_count = {}", Arc::strong_count(&data));
12 | }
编译:成功
运行(退出码 0):
线程 0 求和 = 3
线程 1 求和 = 7
线程 2 求和 = 11
线程 3 求和 = 15
全部线程结束后 strong_count = 1 源码:
1 | use std::sync::Arc;
2 | fn main() { let a = Arc::new(5); *a += 1; }
编译:失败
error[E0594]: cannot assign to data in an `Arc`
--> <实验目录>/src/u30b.rs:2:34
|
2 | fn main() { let a = Arc::new(5); *a += 1; }
| ^^^^^^^ cannot assign
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<i32>`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0594`.
源码:
1 | use std::sync::{Arc, Mutex};
2 | use std::thread;
3 | fn main() {
4 | let counter = Arc::new(Mutex::new(0u64));
5 | let hs: Vec<_> = (0..4).map(|_| { let c = Arc::clone(&counter); thread::spawn(move || { for _ in 0..10_000 { *c.lock().unwrap() += 1; } }) }).collect();
6 | for h in hs { h.join().unwrap(); }
7 | println!("4 个线程各加 10000 次,counter = {}(期望 40000)", *counter.lock().unwrap());
8 | }
编译:成功
运行(退出码 0):
4 个线程各加 10000 次,counter = 40000(期望 40000)Mutex 的细节见Mutex 与 RwLock。
源码:
1 | use std::cell::Cell;
2 | use std::sync::Arc;
3 | use std::thread;
4 | fn main() {
5 | let a = Arc::new(Cell::new(1));
6 | let b = Arc::clone(&a);
7 | thread::spawn(move || b.set(2)).join().unwrap();
8 | }
编译:失败
error[E0277]: `Cell<i32>` cannot be shared between threads safely
--> <实验目录>/src/u30d.rs:7:19
|
7 | thread::spawn(move || b.set(2)).join().unwrap();
| ------------- ^^^^^^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: the trait `Sync` is not implemented for `Cell<i32>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
= note: required for `Arc<Cell<i32>>` to implement `Send`
note: required because it's used within this closure
--> <实验目录>/src/u30d.rs:7:19
|
7 | thread::spawn(move || b.set(2)).join().unwrap();
| ^^^^^^^
note: required by a bound in `spawn`
--> <rust 标准库源码>/library/std/src/thread/functions.rs:128:8
|
125 | pub fn spawn<F, T>(f: F) -> JoinHandle<T>
| ----- required by a bound in this function
...
128 | F: Send + 'static,
| ^^^^ required by this bound in `spawn`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`. 编译:成功
运行(退出码 0):
Rc::clone + drop 每次约 3.98 ns
Arc::clone + drop(单线程) 每次约 3.98 ns
4 个线程各克隆 2000000 次,共用同一个 Arc: 墙钟 272 ms
4 个线程各克隆 2000000 次,各用自己的 Arc: 墙钟 8 ms
共用同一个计数器比各用各的慢 34.1 倍(我的推断:同一个计数器所在的缓存行在 4 个核之间来回传递,没有用性能计数器验证)原子操作与内存顺序见原子类型与内存顺序。