其他
不易察觉的陷阱——Rust中的锁
最近写了个小工具,用到了锁来对共享资源实行互斥访问,无奈总是出现一些莫名奇妙的问题。
为了弄清楚原因,写了个小的测试程序来排查,结果让我对Rust中的锁有了更深的认识。
以下是一个简单的例子, 两个线程共享一个锁
fn main() {
let lock = Arc::new(Mutex::new(()));
let l1 = lock.clone();
let l2 = lock.clone();
std::thread::spawn(move ||{
println("thread 1 accure begin run");
l1.lock().unwrap();
println("thread 1 accure lock success..");
std::thread::sleep(Duration::from_secs(5));
println("thread 1 release lock..");
});
std::thread::sleep(Duration::from_secs(1));
std::thread::spawn(move ||{
println("thread 2 begin run");
l2.lock().unwrap();
println("thread 2 accure lock success..");
std::thread::sleep(Duration::from_secs(3));
println("thread 2 release lock..");
});
std::thread::sleep(Duration::from_secs(11));
println("main thread done");
}
fn println(msg: &str){
println!("{} {}", Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), msg);
}
这段代码看起来没啥问题,应该可以实现互斥访问,也就是线程2只有在线程1释放锁后,才会拿到锁,然而结果确出乎我意料,以下是输出:
2022-03-19 13:04:25 thread 1 accure begin run
2022-03-19 13:04:25 thread 1 accure lock success..
2022-03-19 13:04:26 thread 2 begin run
2022-03-19 13:04:26 thread 2 accure lock success..
2022-03-19 13:04:29 thread 2 release lock..
2022-03-19 13:04:30 thread 1 release lock..
2022-03-19 13:04:37 main thread done
可以看到线程2在线程1 "持有" 锁时,仍然能够 "拿到" 锁, 不应该呀,冥思苦想了一阵后,想起了 Rust中,变量的生命周期问题,rust的变量出了作用域,就会自动销毁。
而锁也应该遵循这个原则,上面的代码中,我并未使用变量接收锁对象,只是简单调用
l1.lock().unwrap(); //这里获取锁对象后,并未用变量接收
所以可能是这个原因导致获取锁对象后,该对象里面就被回收了,所以锁没有成功。
带着这个猜测改写了代码
fn main() {
let lock = Arc::new(Mutex::new(()));
let l1 = lock.clone();
let l2 = lock.clone();
std::thread::spawn(move ||{
println("thread 1 accure begin run");
let v = l1.lock().unwrap();
println("thread 1 accure lock success..");
std::thread::sleep(Duration::from_secs(5));
println("thread 1 release lock..");
});
std::thread::sleep(Duration::from_secs(1));
std::thread::spawn(move ||{
println("thread 2 begin run");
let v = l2.lock().unwrap();
println("thread 2 accure lock success..");
std::thread::sleep(Duration::from_secs(3));
println("thread 2 release lock..");
});
std::thread::sleep(Duration::from_secs(11));
println("main thread done");
}
fn println(msg: &str){
println!("{} {}", Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), msg);
}
果不其然,猜测是对的,输出如下
2022-03-19 13:18:32 thread 1 accure begin run
2022-03-19 13:18:32 thread 1 accure lock success..
2022-03-19 13:18:33 thread 2 begin run
2022-03-19 13:18:37 thread 1 release lock..
2022-03-19 13:18:37 thread 2 accure lock success..
2022-03-19 13:18:40 thread 2 release lock..
2022-03-19 13:18:44 main thread done
改了代码后,锁对象 v的生命周期直到代码块结束,只有线程1执行完代码块后,v才会被销毁,锁也才会释放。
注意到上面的v,并没有使用,能不能用匿名变量接收呢,如下
let _ = l1.lock().unwrap();
改了代码, 运行后
2022-03-19 13:22:52 thread 1 accure begin run
2022-03-19 13:22:52 thread 1 accure lock success..
2022-03-19 13:22:53 thread 2 begin run
2022-03-19 13:22:53 thread 2 accure lock success..
2022-03-19 13:22:56 thread 2 release lock..
2022-03-19 13:22:57 thread 1 release lock..
2022-03-19 13:23:04 main thread done
结果,锁再次失效,看来是不能用匿名变量来接收锁对象了。
总结
锁对象必须用变量接收,否则锁会立即被销毁,锁也就失效了
锁对象不能用匿名变量 '_' 接收, Rust中匿名变量也是声明后立马drop掉,这
样锁也会失效综合#1,#2 锁必须要按以下格式使用
let lock_obj = mylock.lock().unwrap()
.... you code
往期推荐
• Rust编程之道
欢迎关注我的公众号“码中人”,原创技术文章第一时间推送。