You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试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共享时无法提供可变引用而失败。

请问还有其他可行的解决方案吗?


解决方案

问题核心有两点:

  1. insert_key方法错误地声明了&mut self,但内部的Mutex和AtomicU32已经保证了线程安全,不需要外部可变引用。
  2. 多线程共享实例需要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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 21:24:58