Rust 作为一种现代化的系统编程语言,提供了丰富的同步原语来处理并发编程中的各种问题。本章将详细介绍 Rust 中的主要同步原语,包括互斥锁、条件变量、原子类型、读写锁等。
互斥锁 (Mutex)
互斥锁是一种最基本的同步原语,用于控制对共享资源的访问。Rust 标准库中的 std::sync::Mutex 类型可以用来实现这一功能。
创建 Mutex
创建一个互斥锁需要使用 Mutex<T> 类型,其中 T 是你需要保护的数据类型。例如:
// www.javascriptcn.com code example
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0));
for _ in 0..10 {
let data = Arc::clone(&data);
thread::spawn(move || {
let mut num = data.lock().unwrap();
*num += 1;
});
}
thread::sleep(std::time::Duration::from_secs(1));
}使用 Mutex
获取互斥锁需要调用 lock() 方法,该方法会返回一个互斥锁的守护者(Guard),这个守护者提供了对数据的访问权限。如果当前有其他线程已经持有互斥锁,则 lock() 方法将会阻塞,直到该互斥锁被释放。
锁的超时和尝试获取
为了防止程序因等待互斥锁而永久阻塞,Rust 提供了 try_lock() 方法,它会立即返回一个结果,而不是阻塞。此外,还可以使用带有超时限制的锁操作:
// www.javascriptcn.com code example
use std::sync::{Mutex, Arc};
use std::thread;
use std::time::Duration;
fn main() {
let data = Arc::new(Mutex::new(0));
let result = data.lock().unwrap();
println!("Lock acquired successfully");
match data.try_lock() {
Ok(guard) => println!("Lock acquired without blocking"),
Err(e) => println!("Failed to acquire lock: {}", e),
}
// 使用带有超时的锁
match data.try_lock_for(Duration::from_millis(500)) {
Ok(guard) => println!("Lock acquired within timeout"),
Err(e) => println!("Timeout expired while trying to acquire lock"),
}
}条件变量 (Condvar)
条件变量通常与互斥锁一起使用,用于更精细地控制线程之间的协作。
创建 Condvar
在 Rust 中,条件变量是通过 std::sync::Condvar 实现的。通常与互斥锁一起使用:
// www.javascriptcn.com code example
use std::sync::{Mutex, Condvar, Arc};
use std::thread;
fn main() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = Arc::clone(&pair);
thread::spawn(move || {
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap();
*started = true;
cvar.notify_one();
});
let (lock, cvar) = &*pair;
let mut started = lock.lock().unwrap();
// Wait until the other thread is ready.
while !*started {
started = cvar.wait(started).unwrap();
}
}使用 Condvar
条件变量主要提供两种操作:wait() 和 notify_*()。wait() 方法会在条件不满足时阻塞当前线程,而 notify_*() 方法则用于唤醒等待的线程。
原子类型 (Atomic)
原子类型允许在多线程环境中安全地进行读取和修改操作,而无需使用互斥锁或其他同步原语。
创建 Atomic 类型
Rust 标准库提供了多种原子类型,如 AtomicBool, AtomicUsize 等。这些类型可以直接从 std::sync::atomic 模块中导入:
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let counter = AtomicUsize::new(0);
// 原子加法
counter.fetch_add(1, Ordering::SeqCst);
}使用 Ordering 控制内存顺序
Ordering 枚举定义了不同的内存顺序模型,以确保跨线程操作的一致性和安全性。常见的 Ordering 包括 SeqCst, Acquire, Release 等。
原子比较并交换
compare_and_swap 方法可以用来实现原子性的比较和交换操作:
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic_value = AtomicUsize::new(0);
let old_value = atomic_value.compare_and_swap(0, 1, Ordering::SeqCst);
println!("Old value was {}", old_value);
}读写锁 (RwLock)
读写锁允许多个读线程同时访问共享资源,但写线程独占资源。这种机制可以提高并发度。
创建 RwLock
使用 std::sync::RwLock 类型来创建读写锁:
// www.javascriptcn.com code example
use std::sync::{RwLock, Arc};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(vec![1, 2, 3]));
for i in 0..10 {
let data = Arc::clone(&data);
thread::spawn(move || {
if i % 2 == 0 {
let read_data = data.read().unwrap();
println!("Read: {:?}", *read_data);
} else {
let mut write_data = data.write().unwrap();
write_data.push(i);
}
});
}
thread::sleep(std::time::Duration::from_secs(1));
}使用 RwLock
读锁通过 read() 方法获取,写锁通过 write() 方法获取。读锁可以在多个线程中同时持有,而写锁则独占资源。
以上就是 Rust 中的一些基本同步原语。通过合理地使用这些工具,你可以有效地管理并发程序中的资源访问,从而构建出高效且安全的系统。