在Rust
中常有配置读写的操作,一般可以用Arc<RwLock<T>>
来实现,也可以用arc-swap
来。有什么不同?
下面拿一个例子来说明下
假设有个配置,会有一个read thread不断获取配置,配置更新时要实时反映出来。
RwLock
用RwLock
实现一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| use std::sync::{Arc, RwLock}; use std::thread; use std::time::Duration;
fn main() { let config = Arc::new(RwLock::new("Initial Config".to_string())); let shared_config = Arc::clone(&config);
let reader_thread = { thread::spawn(move || loop { let current_config = shared_config.read().unwrap(); println!("Reader thread: {}", *current_config); thread::sleep(Duration::from_secs(2)); }) };
thread::sleep(Duration::from_secs(1));
println!("Updating config"); let mut current_config = config.write().unwrap(); *current_config = "Updated Config".to_string(); println!("Updating config done"); reader_thread.join().unwrap(); }
|
但如果你执行会发现,2修改配置后,1读取配置还是没有变化,而且不会打印Updating config done
, 为啥呢?
这里其实是一个错误示范,RwLock
读会持有锁,如果不释放锁,可以加多个读锁,但是不能获取写锁来修改。
修改代码,用作用域来释放锁
1 2 3 4 5 6 7 8 9 10 11 12 13
| { let current_config = shared_config.read().unwrap(); println!("Reader thread: {}", *current_config); }
...
{ let mut current_config = config.write().unwrap(); *current_config = "Updated Config".to_string(); println!("Updating config done"); }
|
现在结果就满足预期了:
1 2 3 4 5
| Reader thread: Initial Config Updating config Updating config done Reader thread: Updated Config ...
|
arc-swap
这样使用RwLock
修改配置需要注意锁的持有和释放,本质上是无法原子操作读写,导致代码实现上不够友好,也容易写出死锁的代码。
arc-swap
主要就是为了优化这点,原子操作资源,让你可以直接编写读写操作而无需顾虑锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| use arc_swap::ArcSwap; use std::sync::Arc; use std::thread; use std::time::Duration;
fn main() { let config = Arc::new("Initial Config".to_string()); let shared_config = Arc::new(ArcSwap::new(config));
let reader_thread = { let shared_config = Arc::clone(&shared_config); thread::spawn(move || loop { let current_config = shared_config.load(); println!("Reader thread: {}", current_config); thread::sleep(Duration::from_secs(1)); }) };
thread::sleep(Duration::from_secs(2));
let new_config = Arc::new("Updated Config".to_string()); shared_config.store(new_config);
reader_thread.join().unwrap(); }
|
怎么样,代码是不是直截了当了许多?
一般很多读多写少的场景都可以试试arc-swap
。
如有疑问,请文末留言交流或邮件:newbvirgil@gmail.com
本文链接 : https://newbmiao.github.io/2024/05/31/rust-arc-swap.html