当前位置: 首页 > 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。
http://www.lqws.cn/news/90811.html

相关文章:

  • 【灵动Mini-F5265-OB】vscode+gcc工程创建、下载、调试
  • React从基础入门到高级实战:React 高级主题 - React 微前端实践:构建可扩展的大型应用
  • 如何监测光伏系统中的电能质量问题?分布式光伏电能质量解决方案
  • 【论文解读】ReAct:从思考脱离行动, 到行动反馈思考
  • 【Doris基础】Apache Doris中的Fragment概念详解
  • Vue3中使用Echarts图表步骤-demo
  • 通信算法之281:大疆DJI无人机ID-DJI DroneID开源工程-相关问题-协议信息问题
  • 20250603在荣品的PRO-RK3566开发板的Android13下的命令行查看RK3566的温度
  • 学习路之PHP--easyswoole使用视图和模板
  • 大语言模型评测体系全解析(上篇):基础框架与综合评测平台
  • 用户管理页面(解决toggleRowSelection在dialog用不了的隐患,包含el-table的plus版本的组件)
  • 剑指offer15_数值的整数次方
  • Elasticsearch | 如何将修改已有的索引字段类型并迁移数据
  • 云原生周刊:探索 Gateway API v1.3.0
  • 点击启动「高效模式」:大腾智能 CAD 重构研发设计生产力
  • Go 为何天生适合云原生?
  • 项目前置知识——不定参以及设计模式
  • MYSQL索引详解
  • 平台化 LIMS 系统架构 跨行业协同与资源共享的实现路径
  • Ubuntu 22.04 安装 Nacos 记录
  • ubuntu 20.04挂载固态硬盘
  • Ubuntu22.04安装MinkowskiEngine
  • 安装和配置 Nginx 和 Mysql —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录6
  • 解决 Ubuntu 20.04 虚拟机中 catkin_make 编译卡死问题
  • seafile:ubuntu搭建社区版seafile12.0
  • Starrocks Full GC日志分析
  • Stone 3D新版本发布,添加玩家控制和生物模拟等组件,增强路径编辑功能,优化材质编辑
  • 无人机避障——感知部分(Ubuntu 20.04 复现Vins Fusion跑数据集)胎教级教程
  • 网络安全厂商F5推出AI Gateway,化解大模型应用风险
  • RequestRateLimiterGatewayFilterFactory