在多线程编程中,为了避免数据竞合,使用锁是很常用的手段。使用了锁,数据就被保护在“保险箱“内,保证数据不会同时被两个线程修改,导致数据出现错乱。下面分别介绍互斥锁、读写锁和自旋锁。
互斥锁
互斥就是只有一个人能拿到资源,并对资源进行操作,其他人等待资源被释放后才能获取资源的使用权。为了加深理解,举个例子:资源好比是图书馆里的《21xx》书籍(只有一本),该本书被A同学借走了,借这个动作好比是”上锁“。B同学也到图书馆借《21xx》这本书,发现被借走了,只能等待书籍归还后才能借这本书。A同学看完后归还了书籍,归还的动作好比是”释放锁“。之后书籍才能被B同学借走。这个过程如下:
A 借走图书 |
.... | B 借图书,等待...
看书 |
.... |
A 归还图书 |
.... | B 借走图书
对应到编程中,过程如下:
lock()
do somthing
unlock()
Rust 实例:
fn main() {
let s = std::sync::Arc::new(std::sync::Mutex::new("hello".to_owned()));
let sc = s.clone();
let hdl = std::thread::spawn(move || {
// 获取锁
sc.lock().unwrap().push_str(" thread ");
// 释放锁
});
{
// 获取锁
s.lock().unwrap().push_str(" main ");
// 释放锁
}
hdl.join().unwrap();
println!("{:?}", s);
}
读写锁
锁的作用是保证资源不会同时被多个线程修改。读锁则允许过个线程同时对资源进行读取,读取操作本身并没有改变资源的状态,是被允许的。而写锁则可能对资源进行修改,所以只允许一个线程获取写锁。概括为一句话”多读者或单写者“。回想一下Rust的所有权机制,可以同时有多个不变引用 或 一个可变引用,这样也是为了避免数据竟合,与读写锁的机制一样。
为了加深理解,举个例子:
-
A, B, C 三人一起去动物园,三个人都成功购买了门票,能够一起进入动物园游完。购买门票则是获取读锁, 这是多读者的情况。
-
A, B, C 三人一起去动物园,A成功购买了门票,进入了动物园, 但当B购买门票时,发生动物逃脱情况,需要清空动物园,进行处理。此时驯兽大师D(写者)登场,在门外等待的有B, C, D 三人。三人中D的优先级最高,但需要等待A出动物园后才能进去处理。写者的优先级比读者高,写者间的优先级一样,写者需要等待读者或其他写者释放锁后才能获取锁。
Rust实例:
fn main() {
let s = std::sync::Arc::new(std::sync::RwLock::new("hello".to_owned()));
let sc = s.clone();
let hdl = std::thread::spawn(move || {
{
// 获取读锁
println!("{}", sc.read().unwrap());
// 释放读锁
}
// 获取写锁
sc.write().unwrap().push_str(" thread ");
// 释放写锁
});
{
// 获取写锁
s.write().unwrap().push_str(" main ");
// 释放写锁
}
hdl.join().unwrap();
println!("{:?}", s);
}
自旋锁
自旋锁和互斥锁类似,不同点是互斥锁无法获取锁时会让出cpu并阻塞等待(一般不占用cpu资源),而自旋锁则是原地等待,不会让出cpu直到获取锁为止。自旋锁一般用于能够快速处理的操作,减少互斥锁阻塞带来的性能损失。
Rust 实例:
// [dependencies]
// spin = "0.5"
extern crate spin;
fn main() {
let s = std::sync::Arc::new(spin::Mutex::new("hello".to_owned()));
let rs = std::sync::Arc::new(spin::RwLock::new("hello".to_owned()));
let sc = s.clone();
let rsc = rs.clone();
let hdl = std::thread::spawn(move || {
// 获取锁
sc.lock().push_str(" thread ");
rsc.write().push_str(" thread ");
// 释放锁
});
{
// 获取锁
s.lock().push_str(" main ");
{
let st = rs.read();
println!("{}", *st);
}
rs.write().push_str(" main ");
// 释放锁
}
hdl.join().unwrap();
println!("{:?}", s);
println!("{:?}", rs);
}