基本信息
文件名称:如何避免go的map竞态问题的方法.docx
文件大小:18.43 KB
总页数:8 页
更新时间:2025-05-21
总字数:约4.46千字
文档摘要

如何避免go的map竞态问题的方法

//@authyezibin2025-02-0513:08:17

//@paramdatamap[string]interface{}description

func(m*Mmap)Set(datamap[string]interface{}){

m.Mu.Lock()

deferm.Mu.Unlock()

fork,v:=rangedata{

m.Data[k]=v

//SetOne

//@description设置配置map数据

//@authyezibin2025-01-2100:10:23

//@paramkeystringdescription

//@paramvalstringdescription

func(m*Mmap)SetOne(key,valstring){

m.Mu.Lock()

deferm.Mu.Unlock()

m.Data[key]=val

}

建议

1、如果属于读多写少的情况,尽量选择读写锁来减少锁住的范围,从而提高读写性能

2、这里推荐将需要用来读写的map变量和锁共同组建一个struct,这样能保证读和写上的是同一把读写锁,同时也方便整合对map变量的操作

3、分片加锁

方案2中虽然加了读写锁,比加一把普通的锁要性能高些,不过锁的粒度还是大了些,当高并发来袭时,写的操作必然会阻塞读的动作,那么有没有办法将锁住的范围缩小一些呢

思路:如果给map里的每个元素加锁,每次修改只是单个元素的锁生效,其他没改到的元素就正常读,这样锁的粒度会更细,这就是分片加锁的原理

这种就是将一把大锁拆成一把把小锁,是一种空间换时间的方法

实现上,已经有人实现了好用的具有分片锁的map,库地址:/orcaman/concurrent-map

import(

cmap/orcaman/concurrent-map

sync

//InitCmap

//@description初始化分片锁的map

//@authyezibin2025-02-0514:08:17

//@return*cmapConfigdescription

funcInitCmap()*cmapConfig{

returncmapConfig{

cmap.New(),

//Set

//@description批量往map写入元素

//@authyezibin2025-02-0514:10:02

//@paramconfigmap[string]interface{}description

func(c*cmapConfig)Set(configmap[string]interface{}){

fork,v:=rangeconfig{

c.Cmap.Set(k,v)

//Get

//@description从map获取元素

//@authyezibin2025-02-0514:10:22

//@paramkstringdescription

//@returninterface{}description

func(c*cmapConfig)Get(kstring)interface{}{

v,ok:=c.Cmap.Get(k)

ifok{

returnv

}else{

returnnil

}

4、go的原生可并发map

最后还会跟大家介绍一个go原生库里就有一个可并发读写的map,这个放在sync库

官方的文档中指出,在以下两个场景中使用sync.Map,会比使用map+RWMutex的方式,性能要好得多:

1、只会增长的缓存系统中,一个key只写入一次而被读很多次;

2、多个goroutine为不相交的键集读、写和重写键值对。

原理:sync.Map结构里有两个字段,一个read,一个dirty。dirty包含read的所有字段,新增字段是写在dirty上,有个miss变量用户访问到read没有,但是dirty有的数据次数

空间换时间。通过冗余的两个数据结构(只读的read字段、可写的dirty),来减少加锁对性能的影响。对只读字段(read)的操作不需要加锁。优先从read字段读取、更新、删除,因为对read字段