Rust工具集之arc-swap

文章目录

  1. 1. RwLock
  2. 2. arc-swap

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 {
// 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
2
3
4
5
6
7
8
9
10
11
12
13
// 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");
}

现在结果就满足预期了:

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