Rust多线程同步库spinning的使用,高性能自旋锁和同步原语实现并发控制
Rust多线程同步库spinning的使用,高性能自旋锁和同步原语实现并发控制
简介
spinning-rs是一个#![no_std]
库,用于实现自旋锁(spinlocks),功能类似于spin
库,但提供了增强特性如SIX(shared-intent-exclusive)读写锁,以及与lock_api
的集成。
安装
在项目目录中运行以下Cargo命令:
cargo add spinning
或者在Cargo.toml中添加:
spinning = "0.1.0"
使用示例
基本自旋锁使用
use spinning::Spin;
fn main() {
// 创建一个Spin保护的数据
let data = Spin::new(0);
// 获取锁并修改数据
{
let mut guard = data.lock();
*guard += 1;
} // 锁在这里自动释放
// 读取数据
let value = *data.lock();
println!("Data value: {}", value); // 输出: Data value: 1
}
读写锁示例
use spinning::{RwSpin, RwSpinReadGuard, RwSpinWriteGuard};
fn main() {
let data = RwSpin::new(0);
// 多个读取者可以同时访问
{
let read_guard1: RwSpinReadGuard<_> = data.read();
let read_guard2: RwSpinReadGuard<_> = data.read();
println!("Readers see: {}, {}", *read_guard1, *read_guard2);
}
// 单个写入者独占访问
{
let mut write_guard: RwSpinWriteGuard<_> = data.write();
*write_guard += 1;
}
}
完整并发控制示例
use spinning::Spin;
use std::sync::Arc;
use std::thread;
fn main() {
let counter = Arc::new(Spin::new(0));
let mut handles = vec![];
// 创建10个线程并发增加计数器
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..1000 {
let mut num = counter.lock();
*num += 1;
}
});
handles.push(handle);
}
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
println!("Final counter value: {}", *counter.lock());
// 正确情况下应该输出: Final counter value: 10000
}
完整示例demo
下面是一个使用spinning库实现生产者消费者模型的完整示例:
use spinning::{RwSpin, RwSpinReadGuard, RwSpinWriteGuard};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
// 共享缓冲区结构
struct SharedBuffer<T> {
data: RwSpin<Vec<T>>,
capacity: usize,
}
impl<T> SharedBuffer<T> {
fn new(capacity: usize) -> Self {
SharedBuffer {
data: RwSpin::new(Vec::with_capacity(capacity)),
capacity,
}
}
// 生产者写入数据
fn produce(&self, item: T) {
loop {
{
let mut buffer = self.data.write();
if buffer.len() < self.capacity {
buffer.push(item);
return;
}
}
thread::sleep(Duration::from_millis(10)); // 缓冲区满时短暂等待
}
}
// 消费者读取数据
fn consume(&self) -> Option<T> {
let mut buffer = self.data.write();
if !buffer.is_empty() {
Some(buffer.remove(0))
} else {
None
}
}
// 查看缓冲区大小
fn size(&self) -> usize {
let buffer = self.data.read();
buffer.len()
}
}
fn main() {
let buffer = Arc::new(SharedBuffer::<i32>::new(10));
let mut handles = vec![];
// 创建3个生产者线程
for i in 0..3 {
let buffer = buffer.clone();
handles.push(thread::spawn(move || {
for j in 0..20 {
let item = i * 100 + j;
buffer.produce(item);
println!("Producer {} produced: {}", i, item);
}
}));
}
// 创建2个消费者线程
for _ in 0..2 {
let buffer = buffer.clone();
handles.push(thread::spawn(move || {
for _ in 0..30 {
if let Some(item) = buffer.consume() {
println!("Consumer got: {}", item);
} else {
println!("Buffer empty, waiting...");
}
thread::sleep(Duration::from_millis(50));
}
}));
}
// 监控线程
let buffer_clone = buffer.clone();
handles.push(thread::spawn(move || {
for _ in 0..10 {
println!("Current buffer size: {}", buffer_clone.size());
thread::sleep(Duration::from_millis(100));
}
}));
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
println!("Final buffer size: {}", buffer.size());
}
特性
#![no_std]
兼容,适合嵌入式开发- 提供自旋锁(Spin)和读写锁(RwSpin)实现
- 高性能的同步原语
- 与
lock_api
兼容的接口
许可证
spinning-rs采用双重许可:
- Apache License 2.0
- MIT license
可以根据需要选择任一许可证。
1 回复
Rust多线程同步库spinning的使用:高性能自旋锁和同步原语实现并发控制
spinning
是Rust中一个轻量级的高性能同步原语库,它提供了基于自旋的锁和其他同步机制,特别适合在短期锁争用场景下提供更好的性能。
主要特性
- 提供自旋锁(SpinLock)替代标准库的Mutex
- 实现Once(一次性初始化)、Barrier(屏障)、RwLock(读写锁)等同步原语
- 无系统调用开销(在锁争用时间短时性能更好)
- 支持no_std环境
基本用法
首先在Cargo.toml中添加依赖:
[dependencies]
spinning = "0.1"
1. 自旋锁(SpinLock)使用示例
use spinning::SpinLock;
use std::sync::Arc;
use std::thread;
fn main() {
// 创建一个Arc包装的自旋锁,保护一个整数
let lock = Arc::new(SpinLock::new(0));
let mut handles = vec![];
// 创建10个线程并发访问
for _ in 0..10 {
let lock_clone = Arc::clone(&lock);
let handle = thread::spawn(move || {
// 获取锁并修改数据
let mut data = lock_clone.lock();
*data += 1;
});
handles.push(handle);
}
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
// 打印最终结果
println!("Final value: {}", *lock.lock());
}
2. 一次性初始化(Once)使用示例
use spinning::Once;
use std::thread;
// 全局一次性初始化对象
static INIT: Once<()> = Once::new();
fn main() {
let mut handles = vec![];
// 创建5个线程尝试初始化
for i in 0..5 {
handles.push(thread::spawn(move || {
// 只有一个线程会执行初始化代码
INIT.call_once(|| {
println!("Initializing from thread {}", i);
});
}));
}
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
}
3. 读写锁(RwLock)使用示例
use spinning::RwLock;
use std::thread;
fn main() {
// 创建一个读写锁保护的数据
let lock = RwLock::new(5);
// 多个读锁可以同时获取
{
let r1 = lock.read(); // 获取读锁
let r2 = lock.read(); // 可以同时获取另一个读锁
println!("Readers: {}, {}", *r1, *r2);
} // 读锁在这里自动释放
// 使用线程作用域创建写线程
thread::scope(|s| {
// 第一个写线程
s.spawn(|| {
let mut w = lock.write(); // 获取写锁(独占)
*w += 1;
println!("Writer 1: {}", *w);
});
// 第二个写线程
s.spawn(|| {
let mut w = lock.write(); // 获取写锁(独占)
*w += 1;
println!("Writer 2: {}", *w);
});
});
}
性能考虑
自旋锁在以下场景表现良好:
- 锁持有时间非常短
- 线程数量不超过CPU核心数
- 不需要长时间等待
但在以下情况可能不如系统锁:
- 锁争用时间长(会导致CPU空转浪费资源)
- 线程数远多于CPU核心数
高级用法
自定义自旋策略
use spinning::{SpinLock, SpinStrategy};
// 自定义自旋策略
struct MySpinStrategy;
impl SpinStrategy for MySpinStrategy {
fn yield_now() {
// 自定义自旋策略,例如增加一些延迟
std::thread::yield_now();
std::thread::sleep(std::time::Duration::from_nanos(100));
}
}
fn main() {
// 使用自定义策略创建自旋锁
let lock = SpinLock::<i32, MySpinStrategy>::new(0);
let mut data = lock.lock();
*data = 42;
}
无堆分配(no_std)使用
#![no_std]
use spinning::SpinLock;
// 全局静态自旋锁
static GLOBAL_DATA: SpinLock<i32> = SpinLock::new(0);
fn main() {
// 在no_std环境下使用自旋锁
let mut data = GLOBAL_DATA.lock();
*data += 1;
}
注意事项
- 自旋锁可能导致优先级反转问题,使用时需注意
- 长时间持有自旋锁会浪费CPU资源
- 在单核系统上使用自旋锁要特别小心,可能导致死锁
spinning
库为Rust提供了轻量级、高性能的同步原语,特别适合在特定场景下替代标准库的同步机制以获得更好的性能。