4 个版本
0.1.3 | 2023 年 9 月 28 日 |
---|---|
0.1.2 | 2023 年 2 月 24 日 |
0.1.1 | 2023 年 2 月 24 日 |
0.1.0 | 2023 年 2 月 24 日 |
195 在 并发 中排名
每月 206 次下载
18KB
378 行
peace_lock
一个 Mutex/RwLock,如果存在争用则会导致 panic!
peace_lock 帮助您检查并发算法,在禁用检查模式的情况下成为零成本。
动机
一个期望没有争用的锁似乎不合直觉:一开始使用锁的原因是正确管理争用,对吗?是的,也不对。
有时你会在你的数据结构上实现一个无争用算法。你认为并发写入数据结构是可以的,但编译器对此不满意。
为了调试目的,程序应该在检测到任何争用时大声喊叫,这表明存在错误。这使得你在开发期间选择 RwLock
进行健全性检查,并牺牲了一些性能。
后来,你使用 UnsafeCell
和 unsafe
重新编写算法以使其性能更好。
我们能避免重写和不安全的代码吗?
这里有一个具体的例子
你想要一个允许你并发更改值内容的哈希表。你可以这样做,因为你有一个调度算法确保在任何时候都不会有两个线程同时读取和写入同一个值。
let map: HashMap<usize, usize> = ...;
let shared_map = Arc::new(map);
// do these two concurrently in different threads
thread::spawn(|| { *shared_map.get_mut(&1).unwrap() = 3; });
thread::spawn(|| { *shared_map.get_mut(&2).unwrap() = 4; });
上面的代码不会工作,因为你无法在 Arc
内获取可变引用。但“嘿,编译器,这是安全的,我保证!”因为你那出色的调度算法。
为了使编译器容易,你使用了 RwLock
let map: HashMap<usize, RwLock<usize>> = ...;
let shared_map = Arc::new(map);
// do these two concurrently in different threads
thread::spawn(|| { *shared_map.get(&1).unwrap().write() = 3; });
thread::spawn(|| { *shared_map.get(&2).unwrap().write() = 4; });
由于你知道没有冲突,使用 RwLock
不必要地损害了性能。
后来你决定为了性能做一些黑魔法
let map: HashMap<usize, UnsafeCell<usize>> = ...;
let shared_map = Arc::new(map);
// do these two concurrently in different threads
thread::spawn(|| { unsafe { *shared_map.get(&1).unwrap().get() = 3; } });
thread::spawn(|| { unsafe { *shared_map.get(&2).unwrap().get() = 4; } });
代码运行正确,直到调度器产生了争用。
现在你 wonder:我可以将代码重置为 RwLock
以进行健全性检查,但需要重写太多代码。有没有更简单的方法在我之间切换性能模式和健全性检查模式?
这个 crate 就是为此而设计的。
用法
peace_lock 是 std/parking_lot 的 Mutex
和 RwLock
的直接替代品。
将 peace_lock = "0.1"
添加到您的 Cargo.toml 文件中。在调试构建中,检查模式被启用,如果调用 write
或 read
时发生竞争,将直接引发 panic。这表明调度算法存在错误!
use peace_lock::{RwLock, Mutex};
let map: HashMap<usize, RwLock<usize>> = ...;
let shared_map = Arc::new(map);
// do these two concurrently in different threads
// The `write` call will panic if there's another thread writing to the value
// simultaneously.
// The panic behaviour can be disabled by feature flags so that `write` becomes
// zero-cost.
thread::spawn(|| { *shared_map.get(&1).unwrap().write() = 3; });
thread::spawn(|| { *shared_map.get(&2).unwrap().write() = 4; });
// and concurrent read is fine
thread::spawn(|| { shared_map.get(&2).unwrap().read(); });
thread::spawn(|| { shared_map.get(&2).unwrap().read(); });
// also mutex
let map: HashMap<usize, Mutex<usize>> = ...;
let shared_map = Arc::new(map);
thread::spawn(|| { *shared_map.get(&1).unwrap().lock() = 3; });
thread::spawn(|| { *shared_map.get(&2).unwrap().lock() = 4; });
在发布构建中,默认情况下省略了检查。这将使锁无成本。您可以通过将 check
功能添加到功能列表中(例如 peace_lock = { version = "0.1", features = ["check"] }
)显式地打开检查模式。
可选功能
- owning_ref:将
StableAddress
实现添加到锁卫中。 - serde:将
Serialize
和Deserialize
添加到锁中。
需要帮助
如果有人能帮我检查原子排序是否正确使用且不是过于严格,那将非常有帮助。
依赖项
~195KB