- 思路:
- 减少锁粒度:将大的原子统计数据拆分成多个小的部分,每个部分由独立的锁或原子操作管理。这样不同线程可以同时更新不同部分,减少锁争用。
- 使用无锁数据结构:无锁数据结构允许线程在不使用锁的情况下进行并发操作,通过原子操作和内存屏障来保证数据一致性。
- 采用读写锁:如果读操作远多于写操作,可以使用读写锁。读操作可以并发进行,只有写操作需要独占访问。
- 线程本地存储:每个线程维护自己的统计数据副本,减少线程间的竞争。最后再合并这些本地副本。
- Rust特性和方法:
- Atomic类型:Rust标准库中的
std::sync::atomic
模块提供了各种原子类型,如AtomicU32
、AtomicI64
等。这些类型支持原子操作,无需额外的锁就可以安全地在多线程环境中更新。例如:
use std::sync::atomic::{AtomicU32, Ordering};
let counter = AtomicU32::new(0);
counter.fetch_add(1, Ordering::SeqCst);
- **Mutex和RwLock**:`std::sync::Mutex`提供互斥锁,保证同一时间只有一个线程可以访问被保护的数据。`std::sync::RwLock`是读写锁,允许多个线程同时读,但只允许一个线程写。例如:
use std::sync::{Mutex, RwLock};
let data = Mutex::new(vec![0; 10]);
{
let mut data_ref = data.lock().unwrap();
data_ref[0] = 1;
}
let read_write_data = RwLock::new(vec![0; 10]);
{
let read_ref = read_write_data.read().unwrap();
let value = read_ref[0];
}
{
let mut write_ref = read_write_data.write().unwrap();
write_ref[0] = 1;
}
- **无锁数据结构**:虽然Rust标准库没有提供很多无锁数据结构,但有一些第三方库,如`crossbeam`,提供了无锁队列、栈等数据结构。例如,`crossbeam::queue::MsQueue`是一个无锁多生产者 - 单消费者队列。
- **线程本地存储**:`std::thread::LocalKey`可以用于实现线程本地存储。每个线程都有自己独立的数据副本,减少线程间的竞争。例如:
use std::thread;
use std::thread::LocalKey;
static LOCAL_COUNTER: LocalKey<u32> = LocalKey::new();
fn main() {
let handles: Vec<_> = (0..10).map(|_| {
thread::spawn(move || {
let mut local_counter = LOCAL_COUNTER.with(|v| *v.borrow_mut());
local_counter += 1;
})
}).collect();
for handle in handles {
handle.join().unwrap();
}
}