当前位置: 首页 > news >正文

Go基础|map入门

map

map结构

一个map由多个桶构成,每个桶有8个槽位,每个槽位放一个键值对,哈希函数计算出来桶号和槽位号,如槽位被占了则顺序放置,如果满了则创建一个溢出桶放里面。查找就是算出桶和槽位去找,找不到就遍历桶链表。

Go 的 map 是由多个桶(bucket)组成的哈希表结构,每个桶有 8 个槽位(slot),每个槽位存一个 key-value 对。

哈希函数的低位决定数据落在哪个桶,哈希的高位(tophash)帮助定位桶内的槽位。如果槽位被占就顺序查找空位。

如果主桶满了,就分配 溢出桶(overflow bucket),多个桶通过指针串联成链表。

查找时,从主桶开始线性扫描所有槽位,没找到就继续沿 overflow 链遍历,直到找到或链尾。

并发安全

map不能并发使用,如果并发使用需要进行一些处理。

有三种解决方法:1. 使用系统锁(sync.Mutex或sync.RWMutex等);2. 使用sync.Map系统给的安全map;3.使用第三方安全map(分片锁)。

使用系统锁

最常用的方式,go主流框架里面也基本上是用这个方法,一般用的是sync.RWMutex性能好一点;下面是使用Mutex的例子(真实项目代码):

var g errgroup.Group
var mu sync.Mutex // 仍然需要锁
m := make(map[int]string)for i, query := range funArray {index := ig.Go(func() error {s, err := query(univCode)if err != nil {return fmt.Errorf("查询失败: %v", err)}mu.Lock()    // 加锁defer mu.Unlock() // 延迟解锁m[index] = *s // 安全写入return nil})
}

原因:(哪怕index是唯一的)多个 goroutine 同时调用 m[index] = *s 仍然可能导致 map 内部状态损坏,进而引发 panic 或数据不一致。

分片锁是什么?

分片锁是一种 将数据分片,每个分片独立加锁 的并发控制技术。
核心思想:通过减少锁的粒度,降低多线程竞争,提高并发性能。

  • 传统全局锁:所有操作竞争同一把锁(如 sync.Mutex 包裹整个 map)。
  • 分片锁:将数据分成多个块,每个块有自己的锁,线程只需竞争当前操作的块的锁。
// 示例:分片数为 16 的线程安全 map
type ShardedMap struct {shards [16]map[string]interface{}  // 分片数组locks  [16]sync.RWMutex            // 每个分片对应的锁
}// 根据 key 计算分片位置
func (m *ShardedMap) getShard(key string) int {hash := fnv32(key)  // 哈希函数return int(hash) % len(m.shards)
}

二、sync.Map的实现、锁机制和与Go锁的区别

sync.Map

(一)sync.Map的实现
  1. 基本结构
    • sync.Map是一个并发安全的map。它内部维护了一个哈希表,用于存储键值对。和普通map相比,它主要是为了在并发场景下安全地读写数据。
    • 它使用了分段锁(sharded lock)的机制来实现并发控制。将哈希表分为多个段(segment),每个段有自己的锁。这样可以减少锁的粒度,提高并发性能。
  2. 存储和读取过程
    • 在存储键值对时,根据键的哈希值确定存储的段,然后在该段的哈希表中存储键值对。
    • 读取时,也是先根据键的哈希值找到对应的段,然后在该段的哈希表中查找键值对。例如,syncMap.Load("apple")会先找到“apple”对应的段,然后在该段中查找“apple”键对应的值。
(二)锁机制
  1. 锁的类型
    • sync.Map内部使用了互斥锁(mutex)来保护每个段的数据。当对某个段进行写操作(如插入、删除键值对)时,会获取该段的锁;读操作(如查找键值对)也可以获取锁,不过在某些情况下,读操作可以不获取锁,以提高性能。
  2. 锁的作用
    • 锁的作用是保证在并发环境下,对同一个段的操作不会出现数据竞争。例如,当一个线程正在向某个段的哈希表中插入一个键值对时,另一个线程不能同时对该段进行修改操作,否则可能会导致数据不一致。
(三)与Go锁的区别
  1. 锁的粒度
    • Go的sync.Mutex是一个全局锁,它会锁定整个数据结构。而sync.Map使用的是分段锁,锁的粒度更细。例如,对于一个普通的map,如果多个线程同时访问它,可能会因为全局锁而导致性能瓶颈;而sync.Map的分段锁可以让多个线程同时访问不同的段,提高并发性能。
  2. 使用场景
    • sync.Mutex适用于简单的并发控制场景,当需要保护一个共享资源(如一个变量、一个简单的数据结构等)时使用。而sync.Map是专门针对键值对存储的并发场景设计的,它封装了锁的细节,让开发者可以方便地在并发环境下安全地使用map。

相关文章:

  • 代码训练LeetCode(19)轮转数组
  • Prj09--8088单板机C语言8253产生1KHz方波(1)
  • 【Java Web】7.事务管理AOP
  • LeetCode - 144. 二叉树的前序遍历
  • C#学习12——预处理
  • 【PmHub面试篇】Gateway全局过滤器统计接口调用耗时面试要点解析
  • # 将本地UI生成器从VLLM迁移到DeepSeek API的完整指南
  • 【深入 LangChain 的 Model I/O】提示设计、模型调用与输出解析全解析
  • 多数据库学习之星瑞格[SinoDB]数据库安装部署指南
  • 历史记录隐藏的安全风险
  • Windows清理之后,资源管理器卡顿-解决方法
  • Prj10--8088单板机C语言8259测试(1)
  • 服务器被攻击了怎么办
  • Java-IO流之字节输入流详解
  • AJ-Report
  • android NDK 的 -> 是什么意思
  • 【开源工具】Python+PyQt5打造智能桌面单词记忆工具:悬浮窗+热键切换+自定义词库
  • 使用 Golang `testing/quick` 包进行高效随机测试的实战指南
  • GitHub 趋势日报 (2025年06月02日)
  • Splitting Items
  • php做网站访问记录/网站建设与管理就业前景
  • 网站域名到期怎么回事/google官网入口下载
  • 网站建设费用 优帮云/百度排行榜
  • 南京网站开发南京乐识专注/商品seo优化是什么意思
  • 为您服务网站/湖人排名最新
  • 领卷网站怎么做的/企业网站模板html