面试题答案
一键面试use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
let atomic_num = Arc::new(AtomicUsize::new(0));
let atomic_num_clone = atomic_num.clone();
let producer = thread::spawn(move || {
for i in 0..10 {
tx.send(i).unwrap();
}
});
let consumer = thread::spawn(move || {
for num in rx {
atomic_num_clone.fetch_add(num, Ordering::SeqCst);
}
});
producer.join().unwrap();
consumer.join().unwrap();
println!("Final atomic value: {}", atomic_num.load(Ordering::SeqCst));
}
线程安全的保证
- 通道(Channel):通过
std::sync::mpsc
模块创建的通道,用于线程间安全地传递数据。发送端(tx
)和接收端(rx
)在不同线程中使用,确保数据传递的线程安全性。 - 原子类型(AtomicUsize):使用
std::sync::atomic::AtomicUsize
来存储共享数据,它提供了原子操作方法,如fetch_add
。原子操作保证了在多线程环境下对该值的修改不会发生数据竞争。 - Arc(Atomic Reference Counting):使用
Arc
来共享AtomicUsize
实例,Arc
是线程安全的引用计数智能指针,允许在多个线程间安全地共享数据。
原子操作的必要性
在多线程环境下,如果使用普通的usize
类型进行加法操作,不同线程同时对该值进行读取和修改时,可能会发生数据竞争。原子操作提供了一种无锁的机制,确保在多个线程访问共享资源时,操作是原子的,即要么完全执行,要么完全不执行,避免了数据竞争和不一致的问题。例如,fetch_add
方法会原子地将指定的值加到AtomicUsize
实例上,并返回旧值,保证了操作的原子性和线程安全性。