在Rust
中常有配置读写的操作,一般可以用Arc<RwLock<T>>
来实现,也可以用arc-swap
来。有什么不同?
下面拿一个例子来说明下
假设有个配置,会有一个 read thread 不断获取配置,配置更新时要实时反映出来。
文章目录
用RwLock
实现一下
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 {
// 1. read config
let current_config = shared_config.read().unwrap();
println!("Reader thread: {}", *current_config);
thread::sleep(Duration::from_secs(2));
})
};
// Update the configuration after 1s
thread::sleep(Duration::from_secs(1));
println!("Updating config");
// 2. write 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. read config
{
let current_config = shared_config.read().unwrap();
println!("Reader thread: {}", *current_config);
}
...
// 2. write config
{
let mut current_config = config.write().unwrap();
*current_config = "Updated Config".to_string();
println!("Updating config done");
}
现在结果就满足预期了:
Reader thread: Initial Config
Updating config
Updating config done
Reader thread: Updated Config
...
这样使用RwLock
修改配置需要注意锁的持有和释放,本质上是无法原子操作读写,导致代码实现上不够友好,也容易写出死锁的代码。
arc-swap
主要就是为了优化这点,原子操作资源,让你可以直接编写读写操作而无需顾虑锁。
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
。