如何测试Rust中该Node结构体的多线程插入操作?
Rust多线程操作Node结构体的问题及解决方案
问题背景
我实现了一个简易存储结构体Node,它使用AtomicU32原子计数器统计键数量,通过Mutex保护HashMap存储键值对,代码如下:
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}}; use std::collections::HashMap; struct Node { store: Mutex<HashMap<String, String>>, key_count: AtomicU32, } impl Node { fn new() -> Self { Self { store: Mutex::new(Default::default()), key_count: AtomicU32::new(0), } } pub fn insert_key(&mut self, key: String, val: String) -> bool { let mut stored = false; let mut store = self.store.lock().unwrap(); if !store.contains_key(&key) { stored = true; self.key_count.fetch_add(1, AcqRel); store.insert(key, val); } stored } // 此方法不需要互斥锁 pub fn total_keys(&self) -> u32 { self.key_count.load(Ordering::Acquire) } }
尝试编写多线程插入测试时,启动多个线程向同一个Node实例插入值始终失败,已尝试以下几种方式:
将Node移动到线程中
该方法因断言中出现不可变借用问题失败,代码如下:
use std::thread; #[test] fn test_multi_thread_inserts() { let mut node = Node::new(); let threads = 10; let vals = 100; let handlers: Vec<_> = (0..threads).map(|thread_idx| { thread::spawn(move || { for i in 0..vals { &node.insert_key(format!("{thread_idx}-{i}"), format!("val: {thread_idx}-{i}")); } }) }).collect(); for h in handlers { h.join().unwrap(); } assert_eq!(node.total_keys(), threads * vals); }
使用unsafe块
尝试将代码块包裹在unsafe{}中,仍出现相同错误。
使用Arc<Node>
尝试用Arc<Node>克隆后传递给线程执行操作,但因Arc共享时无法提供可变引用而失败。
请问还有其他可行的解决方案吗?
解决方案
问题核心有两点:
insert_key方法错误地声明了&mut self,但内部的Mutex和AtomicU32已经保证了线程安全,不需要外部可变引用。- 多线程共享实例需要
Arc实现原子引用计数,但Arc仅提供不可变引用,需配合修改后的方法签名使用。
步骤1:修正insert_key方法签名
将insert_key的参数从&mut self改为&self——因为Mutex会保证对HashMap的独占访问,AtomicU32的操作本身也是线程安全的:
pub fn insert_key(&self, key: String, val: String) -> bool { let mut stored = false; let mut store = self.store.lock().unwrap(); if !store.contains_key(&key) { stored = true; self.key_count.fetch_add(1, Ordering::AcqRel); store.insert(key, val); } stored }
步骤2:用Arc包装Node实现多线程共享
Arc可以安全地在多个线程间克隆共享,配合修改后的insert_key(仅需&self)即可实现多线程插入:
use std::sync::Arc; #[test] fn test_multi_thread_inserts() { let node = Arc::new(Node::new()); let threads = 10; let vals = 100; let mut handlers = Vec::with_capacity(threads); for thread_idx in 0..threads { let node_clone = Arc::clone(&node); handlers.push(thread::spawn(move || { for i in 0..vals { node_clone.insert_key( format!("{thread_idx}-{i}"), format!("val: {thread_idx}-{i}") ); } })); } for h in handlers { h.join().unwrap(); } assert_eq!(node.total_keys(), threads as u32 * vals as u32); }
额外说明
- 此前使用
Arc<Node>失败的原因是原方法需要&mut self,而Arc仅能提供不可变引用,修改方法签名后即可解决。 - 完全不需要使用
unsafe块,Rust的Arc、Mutex和原子类型足以实现线程安全,unsafe会引入不必要的风险。 - 测试中生成的键(
{thread_idx}-{i}格式)均唯一,因此最终key_count值等于threads * vals,断言会成功。
内容的提问来源于stack exchange,提问作者Uddeshya Singh
相关产品推荐
相关产品推荐

