Go1.9sync.Map揭秘

mac2025-09-27  16

为什么要有sync.Map

顾名思义,sync.Map也是一种map,相当于对原生map做了封装,之所以出现的原因是map不是线程并发安全的。go设计map是考虑到map同时读写的场景不多,所以设计map在进行写操作时是不允许进行其他的操作,否则会报fatal error: concurrent map read and map write错误,示例如下:

package main func main() { m := make(map[int]int) go func() { for { _ = m[1] } }() go func() { for { m[2] = 2 } }() select {} }

在Go 1.9之前解决以上问题是通过加锁的方式,但会影响读写的性能和带来一些其他的问题,这里不详述

在Go1.9版本中新增 sync.Map,支持并发读写,示例如下(慎用,cpu占用较大):

package main import ( "fmt" "sync" "time" ) var m sync.Map func main() { go func() { for { m.Store(2, 2) } }() go func() { for { v, _ := m.Load(2) fmt.Println(v) } }() time.Sleep(1 * time.Second) }

sync.Map的使用

package main import ( "sync" "fmt" ) func main() { //开箱即用 var sm sync.Map //store 方法,添加元素 sm.Store(1,"a") //Load 方法,获得value if v,ok:=sm.Load(1);ok{ fmt.Println(v) } //DELETE方法,删除,没有返回值 sm.Delete(1) //LoadOrStore方法,获取或者保存 //参数是一对key:value,如果该key存在且没有被标记删除则返回原先的value(不更新)和true;不存在则store,返回该value 和false if vv,ok:=sm.LoadOrStore(1,"c");ok{ fmt.Println(vv) } if vv,ok:=sm.LoadOrStore(2,"c");!ok{ fmt.Println(vv) } //遍历该map,参数是个函数,该函数参的两个参数是遍历获得的key和value,返回一个bool值,当返回false时,遍历立刻结束。 sm.Range(func(k,v interface{})bool{ fmt.Print(k) fmt.Print(":") fmt.Print(v) fmt.Println() return true }) }

sync.Map的实现原理

空间换时间。 通过冗余的两个数据结构(只读的read、只写的dirty),实现加锁对性能的影响。对只读字段(read)的操作不需要加锁。优先从 read 字段读取、更新、删除,因为对 read 字段的读取不需要锁动态调整,miss次数多了之后,将dirty数据提升为read。避免总是从 dirty 中加锁读取。double-checking。加锁之后先还要再检查 read 字段,确定真的不存在才操作 dirty 字段。延迟删除。 删除一个键值只是打标记(会将key对应value的pointer置为nil,但read中仍然有这个key:key;value:nil的键值对),只有在提升dirty的时候才清理删除的数据。虽然read和dirty有冗余数据,但这些数据是通过指针指向同一个数据,所以尽管Map的value会很大,但是冗余的空间占用还是有限的。

sync.Map的使用场景

适用于读多,更新多,新建少的场景。

sync.map是以无锁操作一级缓存的方式支持并发、提高性能。 新建情况下,可能会带来较大的开销,比如:读击穿、数据刚从二级缓存同步到一级缓存后,又要新建key,数据又要反向同步一次。

参考文章: Go语言学习——sync.map源码剖析 https://studygolang.com/topics/12363 https://blog.csdn.net/u010230794/article/details/82143179 https://blog.csdn.net/jeffrey11223/article/details/79587015 https://colobu.com/2017/07/11/dive-into-sync-Map/ https://time.geekbang.org/column/article/301174 后半段

最新回复(0)