Rust 实现统计功能
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
let data_blocks: Vec<Vec<i32>> = vec![
vec![1, 2, 3, 2, 1],
vec![2, 3, 4, 2, 3],
vec![1, 3, 5, 1, 3]
];
let target_element = 2;
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for data in data_blocks {
let counter_clone = Arc::clone(&counter);
let handle = thread::spawn(move || {
for num in data {
if num == target_element {
counter_clone.fetch_add(1, Ordering::SeqCst);
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("The target element {} appears {} times.", target_element, counter.load(Ordering::SeqCst));
}
复杂度分析
- 时间复杂度:随着线程数量增加,时间复杂度从 O(n) 变为接近 O(n / t),其中 n 是数据总量,t 是线程数量。因为多个线程并行处理数据块,理论上处理速度会随着线程数增加而加快。但由于线程调度、原子操作的开销,实际加速比会小于线程数。
- 空间复杂度:空间复杂度保持 O(1),因为除了输入数据本身,额外使用的空间(如原子计数器)不随数据量或线程数增加而变化。
性能优化
- 减少原子操作竞争:可以采用分块统计再合并的方式。每个线程维护自己的局部计数器(普通变量),统计完后再合并到全局原子计数器。这样减少了对全局原子计数器的竞争,提高并发性能。
- 线程池复用:使用线程池避免频繁创建和销毁线程的开销。如
thread - pool
库,可以预先创建一定数量的线程,重复使用它们来处理不同的数据块。
- 优化数据结构:如果数据块非常大,可以考虑使用更高效的数据结构,如哈希表来快速查找目标元素,虽然这会增加空间复杂度,但可能提高整体性能。