兄弟们,2026年了,Rust凭借其极致的内存安全已经成为高并发服务端的首选语言。但在实际开发中,很多从Java或Go转过来的同学最头疼的就是“所有权(Ownership)”机制。特别是当你需要开多个线程去共同修改一个HashMap或者计数器时,编译器那无情的红线总是让你怀疑人生。今天咱们就来聊聊,如何正确使用 Arc 和 Mutex,在Rust中写出既安全又高效的多线程代码。

核心痛点:跨线程的数据竞争焦虑

Rust的底层哲学是“没有数据竞争的并发”。如果你试图把一个普通的变量直接 move 进两个不同的 std::thread::spawn 闭包里,编译绝对会报错。因为Rust不允许同一份可变数据被多个所有者同时持有。

实战方案:原子引用计数 + 互斥锁的黄金组合

要在多线程间安全地共享并修改状态,你需要两把钥匙:Arc 解决“多所有权”问题,Mutex 解决“内部可变性”与“并发访问”问题。

代码实战:安全的共享计数器

1use std::sync::{Arc, Mutex};
2use std::thread;
3
4fn main() {
5    // 1. 使用 Mutex 保证同一时刻只有一个线程能修改数据
6    let counter = Arc::new(Mutex::new(0));
7    let mut handles = vec![];
8
9    for _ in 0..10 {
10        // 2. 使用 Arc (Atomic Reference Counting) 实现多线程间的克隆共享
11        let counter_clone = Arc::clone(&counter);
12        
13        let handle = thread::spawn(move || {
14            // 3. lock() 获取锁,如果其他线程持有锁,当前线程会自动阻塞等待
15            let mut num = counter_clone.lock().unwrap();
16            *num += 1;
17        });
18        handles.push(handle);
19    }
20
21    for handle in handles {
22        handle.join().unwrap();
23    }
24
25    println!("最终结果: {}", *counter.lock().unwrap()); // 输出: 10
26}
避坑指南
  • 死锁警告:千万不要在一个持有了锁的作用域里,再去尝试获取另一把锁,这极易导致线程互相等待形成死锁。尽量缩小 lock() 的作用域。
  • 性能考量Mutex 是有开销的。如果是高频读写且只读远多于写入的场景,可以考虑使用 RwLock;如果只是简单的整数累加,直接用 AtomicUsize 会比 Mutex 快得多。

总结:Rust的所有权机制初看反人类,但一旦你习惯了它,你会发现那些曾经困扰你的并发Bug,在编译阶段就被彻底扼杀了。掌握Arc+Mutex,是你迈向Rust高阶玩家的必经之路!

更多推荐