第
如何避免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字段