
在 Rust 的所有权系统中,共享数据是痛点——借用规则严苛,但多线程时代,数据共享是刚需。Arc 就是你的“原子级守护者”:它让不可变数据安全地在多线程间共享,无锁开销,零成本抽象。
为什么 Arc 这么香?想象你的 Web 服务器,多个线程读取同一配置;或游戏引擎,共享场景数据。Arc 让这些变得丝滑:clone 一个 Arc 引用,所有者计数原子递增,数据生命周期自动管理。单线程用 Rc(Reference Counting),多线程必备 Arc(加了原子操作)。
1. Arc 基础:原子引用计数入门Arc 藏在 std::sync::Arc 中。核心:Arc::new(value) 创建,Arc::clone(&arc) 共享引用(不是深拷贝!),计数归零时自动 drop。入门代码:简单共享字符串
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new("Rust Arc: 共享数据的神器!".to_string());
// 创建 3 个线程,共享 data
let mut handles = vec![];
for i in 0..3 {
let data_clone = Arc::clone(&data); // 克隆引用,计数 +1
let handle = thread::spawn(move || {
println!("线程 {} 读取: {}", i, data_clone);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("主线程计数: {}", Arc::strong_count(&data)); // 1,线程结束计数回 1
}输出:
线程 0 读取: Rust Arc: 共享数据的神器!
线程 1 读取: Rust Arc: 共享数据的神器!
线程 2 读取: Rust Arc: 共享数据的神器!
主线程计数: 1关键:Arc::clone 廉价(~原子加法),不像 Vec clone 拷贝数据。strong_count 调试计数,实际少用(race condition 风险)。与 Rc 的区别:Rc 非线程安全(用 std::rc::Rc),Arc 用原子指令(std::sync::atomic),多核无竞争。2. 实际案例一:多线程共享配置场景:CLI 工具启动多个 worker 线程,共享 JSON 配置。单拷贝浪费,Arc 完美。先建 config.json(模拟):
{
"app_name": "MyApp",
"debug": true,
"workers": 4
}代码:异步加载(用 serde),共享给线程。
Cargo add serde --features=derive, serde_json
use std::sync::Arc;
use std::thread;
use std::fs;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Config {
app_name: String,
debug: bool,
workers: usize,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 加载配置
let config_str = fs::read_to_string("config.json")?;
let config: Config = serde_json::from_str(&config_str)?;
let config_arc = Arc::new(config);
// 启动 worker 线程
let mut handles = vec![];
for i in 0..config_arc.workers {
let config_clone = Arc::clone(&config_arc);
let handle = thread::spawn(move || {
if config_clone.debug {
println!("Worker {}: {} 启动,调试模式", i, config_clone.app_name);
}
// 模拟工作
thread::sleep(std::time::Duration::from_millis(100));
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("所有 worker 完成!");
Ok(())
}输出:
Worker 0: MyApp 启动,调试模式
Worker 1: MyApp 启动,调试模式
...
所有 worker 完成!为什么实用? 配置加载一次,共享多线程。扩展:用 tokio::sync::Arc(async 兼容),在异步 worker 中用。性能:clone <1ns,远胜拷贝 JSON。3. 实际案例二:Arc + Mutex 共享可变缓存Arc 只共享不可变数据?加 Mutex 变身共享可变!Arc<Mutex<T>> 是多线程黄金组合:读多用 Arc,写用 lock。
场景:多线程 LRU 缓存,worker 线程读写共享缓存。
简化版:用 HashMap + Mutex。
Cargo add rand
use std::sync::{Arc, Mutex};
use std::thread;
use std::collections::HashMap;
use rand::Rng;
fn main() {
let cache = Arc::new(Mutex::new(HashMap::<String, i32>::new()));
let mut handles = vec![];
for i in 0..5 {
let cache_clone = Arc::clone(&cache);
let handle = thread::spawn(move || {
let mut rng = rand::thread_rng();
let key = format!("key{}", i);
let value = rng.gen_range(1..100);
// 写:锁住缓存
{
let mut cache_guard = cache_clone.lock().unwrap(); // RAII 锁
cache_guard.insert(key.clone(), value);
println!("线程 {} 写入 {}: {}", i, key, value);
} // 锁自动释放
// 读:再锁(或用 RwLock 优化)
{
let cache_guard = cache_clone.lock().unwrap();
if let Some(v) = cache_guard.get(&key) {
println!("线程 {} 读取 {}: {}", i, key, v);
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let final_cache = cache.lock().unwrap();
println!("最终缓存: {:?}", final_cache);
}输出示例:
线程 0 写入 key0: 42
线程 0 读取 key0: 42
线程 1 写入 key1: 73
...
最终缓存: {"key0": 42, "key1": 73, ...}神技:锁作用域最小化(用 {}),防死锁。RwLock 读锁允许多读:Arc<RwLock<T>>。实战:Web 服务器缓存 API 响应,QPS 翻倍!4. 高级技巧:Weak Arc 与循环引用Arc 有 strong/weak 引用:strong 控制生命周期,weak 防循环(像 JS WeakRef)。示例:防循环的父子节点
use std::sync::{Arc, Weak};
use std::rc::Rc; // 对比 Rc 用 Weak<Rc>
#[derive(Debug)]
struct Node {
value: i32,
parent: Option<Weak<Node>>, // Weak 防循环
children: Vec<Arc<Node>>,
}
fn main() {
let parent = Arc::new(Node {
value: 1,
parent: None,
children: vec![],
});
let child = Arc::new(Node {
value: 2,
parent: Some(Arc::downgrade(&parent)), // Weak 引用
children: vec![],
});
parent.children.push(Arc::clone(&child));
// 升级 Weak 到 Arc
if let Some(parent_weak) = &child.parent {
if let Some(parent_up) = parent_weak.upgrade() {
println!("子节点父值: {}", parent_up.value);
} else {
println!("父节点已 drop");
}
}
// drop child,parent 计数不变
println!("Parent 计数: {}", Arc::strong_count(&parent));
}输出:
子节点父值: 1
Parent 计数: 1实用:树形数据结构(如 DOM 或文件系统),Weak 断开循环,避免内存泄漏。5. 注意事项:坑点与最佳实践
多用 std::sync::OnceLock 全局单例共享。Arc,让 Rust 共享如呼吸般自然Arc 是 Rust 并发拼图的灵魂:从不可变共享,到可变守护,它让多线程代码安全又高效。