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

如何在不锁定全部数据的前提下实现多线程共享HashMap?

无锁多线程访问HashMap不同区域的实现方案

你提到的场景确实可以在不依赖全局锁(如Mutex/RwLock)的前提下实现,核心是通过手动保证内存安全的unsafe代码,或者借助细粒度分桶的第三方库,来规避线程间的数据竞争。

方案一:手动unsafe实现(完全无锁)

利用UnsafeCell实现跨线程的内部可变性,再配合Arc共享实例,同时严格保证两个线程访问的是HashMap中完全独立的哈希桶(bucket),从根源避免数据竞争。

示例代码

use std::collections::HashMap;
use std::sync::Arc;
use std::cell::UnsafeCell;
use std::thread;

// 手动实现Sync trait,前提是我们能严格保证线程访问的安全性
unsafe impl<K: Sync, V: Sync> Sync for UnsafeHashMap<K, V> {}

struct UnsafeHashMap<K, V>(UnsafeCell<HashMap<K, V>>);

impl<K, V> UnsafeHashMap<K, V> {
    fn new(map: HashMap<K, V>) -> Self {
        UnsafeHashMap(UnsafeCell::new(map))
    }

    // 可变插入:必须保证当前无其他线程访问该key对应的bucket
    unsafe fn insert(&self, key: K, value: V) -> Option<V> {
        (*self.0.get()).insert(key, value)
    }

    // 不可变读取:必须保证当前无其他线程修改该key对应的bucket
    unsafe fn get(&self, key: &K) -> Option<&V>
    where
        K: Eq + std::hash::Hash,
    {
        (*self.0.get()).get(key)
    }
}

fn main() {
    let mut map = HashMap::new();
    map.insert(2, 5);
    let shared_map = Arc::new(UnsafeHashMap::new(map));

    thread::scope(|s| {
        let map_clone = shared_map.clone();
        s.spawn(move |_| {
            // 手动确保:key=1和key=2哈希到不同bucket,无竞争风险
            unsafe {
                map_clone.insert(1, 5);
            }
        });

        let map_clone = shared_map.clone();
        s.spawn(move |_| {
            unsafe {
                let d = map_clone.get(&2).unwrap();
                println!("读取到值:{}", d);
            }
        });
    }).unwrap();
}

核心注意事项

  • 必须严格隔离哈希桶:如果两个线程访问的key哈希到同一个bucket,哪怕key不同,也会触发数据竞争(HashMap的bucket是链表/红黑树结构,修改和读取会操作共享节点)。可以通过自定义哈希函数强制分离key的哈希分布,或提前验证哈希结果。
  • Sync实现的危险性:上述UnsafeHashMap的Sync是unsafe的,只有在你能100%保证线程访问无冲突时才安全,一旦违反会导致未定义行为。
  • 避免依赖HashMap内部细节:不同Rust版本的HashMap桶数量、哈希算法可能变化,若依赖特定分布,需用HashMap::with_hasher固定哈希器。

方案二:使用细粒度分桶库(安全且近似无锁)

如果不想写手动unsafe代码,可以用社区成熟的分桶HashMap库,比如dashmap。它内部将HashMap拆分为多个shard,每个shard独立加锁,访问不同shard时不会全局阻塞,性能接近无锁,且完全安全。

示例代码

use dashmap::DashMap;
use std::thread;

fn main() {
    let map = DashMap::new();
    map.insert(2, 5);

    thread::scope(|s| {
        s.spawn(|_| {
            map.insert(1, 5);
        });
        s.spawn(|_| {
            let d = map.get(&2).unwrap();
            println!("读取到值:{}", d);
        });
    }).unwrap();
}

dashmap会自动将不同哈希值的key分配到不同shard,无需手动处理线程安全,同时满足你场景中无全局锁的需求。

内容的提问来源于stack exchange,提问作者HyunWoo Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 08:20:53