常见错误
- 资源竞争(Race Condition):
- 描述:多个线程同时访问和修改共享资源,导致最终结果依赖于线程执行的顺序。例如,多个线程同时对一个共享变量进行自增操作,由于操作不是原子的,可能会出现值覆盖等问题,导致结果不正确。
- 示例:假设有一个共享的计数器,多个线程同时对其进行自增操作。
- 线程恐慌(Panic):
- 描述:线程在运行过程中遇到不可恢复的错误,如数组越界访问、解引用空指针等,导致线程崩溃。如果主线程没有适当处理,整个程序可能会终止。
- 示例:在一个线程中尝试访问数组超出其索引范围的元素。
使用Rust机制处理错误
- Mutex(互斥锁):
- 用途:用于保护共享资源,确保同一时间只有一个线程可以访问该资源。
- 用法:通过
Mutex::new
创建一个Mutex
实例来包裹共享资源。线程在访问资源前,需要先获取锁(lock
方法),访问结束后释放锁(锁对象离开作用域时自动释放)。
- 示例:
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Final counter value: {}", *counter.lock().unwrap());
}
- Arc(原子引用计数):
- 用途:用于在多线程环境中共享数据,
Arc
允许在多个线程间安全地共享数据所有权。它结合Mutex
等同步原语,可以让多个线程安全地访问共享资源。
- 用法:通过
Arc::new
创建Arc
实例,使用Arc::clone
来复制引用。配合Mutex
时,先通过Arc
获取到共享资源的引用,再获取Mutex
的锁来访问资源。
- 示例:上述代码中已经展示了
Arc
与Mutex
结合使用的方式,Arc
包裹Mutex
实例,多个线程通过克隆Arc
引用,并获取Mutex
锁来安全地访问共享的计数器。
- RwLock(读写锁):
- 用途:当读操作远远多于写操作时,使用
RwLock
可以提高性能。它允许多个线程同时进行读操作,但只允许一个线程进行写操作。
- 用法:通过
RwLock::new
创建RwLock
实例。读操作使用read
方法获取读锁,写操作使用write
方法获取写锁。
- 示例:
use std::sync::{RwLock, Arc};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(String::from("initial data")));
let mut handles = vec![];
for _ in 0..3 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
let read_data = data.read().unwrap();
println!("Read: {}", read_data);
});
handles.push(handle);
}
let data = Arc::clone(&data);
let write_handle = thread::spawn(move || {
let mut write_data = data.write().unwrap();
*write_data = String::from("new data");
});
handles.push(write_handle);
for handle in handles {
handle.join().unwrap();
}
}
- 条件变量(Condvar):
- 用途:用于线程间的同步通信,允许线程等待某个条件满足。通常与
Mutex
一起使用。
- 用法:创建一个
Condvar
实例,线程通过wait
方法等待条件变量通知,在等待前需要先获取Mutex
锁。其他线程通过notify_one
或notify_all
方法通知等待的线程。
- 示例:
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);
let handle = 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();
while!*started {
started = cvar.wait(started).unwrap();
}
handle.join().unwrap();
}