21个版本 (7个稳定版)
2.1.0 | 2024年7月2日 |
---|---|
1.2.2 | 2024年1月9日 |
1.1.0 | 2023年3月2日 |
1.0.0 | 2022年7月1日 |
0.5.0 | 2022年3月14日 |
#137 在 并发 中
每月956次下载
33KB
529 代码行
ShardedMutex
,原子一切
此库提供全局锁,用于(伪)原子访问数据,无需为每个对象分配内存。通过根据要锁定对象的地址从池中选择Mutex来提高并发性。
每个受保护的类型都有一个Mutex池,因此可以同时锁定不同类型的值。
- 由于是分片的,这些Mutex仍然作为全局和非递归锁。当同一类型/域的锁已被持有时,不要对另一个对象进行锁定,否则将发生死锁。
try_lock()
和try_lock_for()
方法没有此限制,但会在锁已被持有时失败。 multi_lock()
方法允许同时锁定同一类型的多个对象。then_lock()
方法实现了手递锁,在释放已持有的锁之前先获得新对象的锁。
可以使用类型标记为相同类型的不同锁定域。
为实现了Copy
和 PartialEq
的类型提供伪原子访问。这些类型永远不会发生死锁,因为它们始终是叶子锁。
在调试构建中,活动死锁检测器将尝试在同时持有锁的情况下锁定同一类型/域的对象时引发panic。
示例用法
use sharded_mutex::ShardedMutex;
// create 2 values that need locking
let x = ShardedMutex::new(123);
let y = ShardedMutex::new(234);
// a single lock
assert_eq!(*x.lock(), 123);
// Multiple locks
let mut guards = ShardedMutex::multi_lock([&x, &y]);
assert_eq!(*guards[0], 123);
assert_eq!(*guards[1], 234);
// can write as well
*guards[1] = 456;
// unlocks
drop(guards);
// lock again
assert_eq!(*y.lock(), 456);
// Pseudo atomic access
use sharded_mutex::PseudoAtomicOps;
x.store(&234);
assert_eq!(x.load(), 234);
let mut swapping = 345;
x.swap(&mut swapping);
assert_eq!(swapping, 234);
assert_eq!(x.load(), 345);
assert!(!x.compare_and_set(&123, &456));
assert!(x.compare_and_set(&345, &456));
assert_eq!(x.load(), 456);
特性
对齐
ShardedMutex
使用互斥锁数组来锁定对象。这会将与无关对象相关的互斥锁紧密打包,这反过来又会因为假缓存共享而影响性能。为了减轻这个问题,可以增加这些互斥锁的内部对齐。这样做代价是更大的内存占用。
align_none
尽可能紧密地打包互斥锁。对于只有很少缓存或没有缓存的嵌入式系统以及内存昂贵的系统来说,这是一个很好的选择。
align_narrow
这是默认设置,它将每个缓存行放置8个互斥锁,这应该在空间和性能之间提供良好的折衷。
align_wide
每个缓存行放置4个互斥锁,应该进一步提高性能。可能只有在证明存在缓存竞争时才需要。
align_cacheline
每个缓存行放置一个互斥锁。这应该在没有缓存竞争的情况下提供最佳性能,但代价是浪费内存。
池大小
当对互斥锁的竞争很小的时候,锁定性能最佳。我们通过将访问分散到互斥锁池中来实现这一点。这些池的大小可以根据预期将同时访问互斥锁的线程数量进行调整。池大小是梅森素数,以便均匀地分散互斥锁的负载。
normal_pool_size
互斥锁池有127个条目。这应该适用于大多数应用程序。这是默认设置。
small_pool_size
互斥锁池有31个条目。这可能会严重限制并发性。仅在内存非常宝贵(嵌入式)或只有少数线程尝试锁定对象时使用。
huge_pool_size
互斥锁池有8191个条目。适用于高度并发系统,具有许多核心,数百到数千个线程同时锁定对象。
依赖项
~0.4–5MB
~11K SLoC