面试题答案
一键面试设计思路
- 本地统计:在每个节点上,使用原子类型来统计不同类型的事件数量,以确保多线程环境下的安全操作。原子类型可以提供原子操作,避免数据竞争。
- 减少竞争:
- 选择合适的原子类型:根据实际需求,例如对于整数类型的事件计数,可以使用
AtomicU64
。AtomicU64
提供了针对64位无符号整数的原子操作。 - 同步机制:采用
Mutex
或RwLock
来保护共享资源。在读取统计数据时,如果不需要修改,可以使用RwLock
的读锁,允许多个线程同时读取,减少锁竞争。在修改统计数据时,使用Mutex
或RwLock
的写锁。
- 选择合适的原子类型:根据实际需求,例如对于整数类型的事件计数,可以使用
- 汇总数据:每个节点定期将本地统计数据发送到中央节点。可以使用网络通信库(如
tokio
+tcp
)来实现数据传输。
代码实现
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;
// 定义事件类型
enum EventType {
TypeA,
TypeB,
// 可以添加更多类型
}
// 每个节点的本地统计数据结构
struct NodeStatistics {
event_counts: Arc<RwLock<Vec<AtomicU64>>>,
}
impl NodeStatistics {
fn new() -> Self {
let mut counts = Vec::new();
// 为每种事件类型初始化计数
counts.push(AtomicU64::new(0));
counts.push(AtomicU64::new(0));
NodeStatistics {
event_counts: Arc::new(RwLock::new(counts)),
}
}
// 增加事件计数
fn increment(&self, event_type: EventType) {
let mut counts = self.event_counts.write().unwrap();
match event_type {
EventType::TypeA => {
counts[0].fetch_add(1, Ordering::SeqCst);
}
EventType::TypeB => {
counts[1].fetch_add(1, Ordering::SeqCst);
}
}
}
// 获取当前统计数据
fn get_statistics(&self) -> Vec<u64> {
let counts = self.event_counts.read().unwrap();
counts.iter().map(|count| count.load(Ordering::SeqCst)).collect()
}
}
fn main() {
let statistics = NodeStatistics::new();
let stats_clone = Arc::clone(&statistics.event_counts);
// 模拟多个线程产生事件
let mut handles = Vec::new();
for _ in 0..10 {
let stats = Arc::clone(&stats_clone);
let handle = thread::spawn(move || {
for _ in 0..100 {
let random_event = if rand::random::<bool>() {
EventType::TypeA
} else {
EventType::TypeB
};
let mut local_counts = stats.write().unwrap();
match random_event {
EventType::TypeA => {
local_counts[0].fetch_add(1, Ordering::SeqCst);
}
EventType::TypeB => {
local_counts[1].fetch_add(1, Ordering::SeqCst);
}
}
}
});
handles.push(handle);
}
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
// 获取并打印统计数据
let stats = statistics.get_statistics();
println!("Type A count: {}", stats[0]);
println!("Type B count: {}", stats[1]);
}
代码说明
NodeStatistics
结构体:包含一个Arc<RwLock<Vec<AtomicU64>>>
来存储每种事件类型的计数。Arc
用于在多个线程间共享数据,RwLock
提供读写锁机制,Vec<AtomicU64>
存储每种事件类型的原子计数。increment
方法:增加指定类型事件的计数,通过fetch_add
原子操作确保线程安全。get_statistics
方法:获取当前的统计数据,使用读锁允许多个线程同时读取。main
函数:创建NodeStatistics
实例,并启动多个线程模拟事件产生,最后获取并打印统计数据。
在实际的分布式系统中,还需要添加网络通信部分将本地统计数据发送到中央节点,这里为了聚焦于原子操作和同步机制,未包含网络通信代码。例如可以使用tokio
库来实现异步网络通信。