课程章节:4-7,4-8 怎么解决map的并发问题?
课程讲师:Moody
课程内容:
△ map的读写是有并发问题的,如果A协程正在读取,而B协程正在修改,就会引起数据不一致的问题
△普通map可以通过加锁 mutex来解决,但是性能差,不能并发读写
△sync.map 可以解决并发读写问题
type Map struct {
// 锁
mu Mutex
// 只读。其内部是一个readOnly,readOnly实际上是一个map[any]*entry,如果是1.8版本之前
,是一个map[interface{}]*enty,any是interface{}的别名
read atomic.Value // readOnly
// 脏map,主要是维持一个脏数据,用于map新增元素
dirty map[any]*entry
misses int
}
type readOnly struct {
m map[any]*entry
//追加
amended bool // true if the dirty map contains some key not in m.
}
整体结构是由read和dirty各维持一组key,而他们的value是存在一起的;
※ 正常读、改:进入到read指向的结构体,然后进入m,m里面存的key,通过key的指针拿到最终的value;
※ 追加:新增的时候,先判断是否在m里存在,如果m里存在那就是修改值。不在m里,先给mu上锁,主要是所dirty,同时只有一个协程操作dirty。同时,read里面的amended=true,此flag的意思是m已经不完整了,整个map里有追加数据。dirty增加新的key,做万能指针,并给万能指针指向一个新key对应的value。
※追加后读:先读m,如果m里面没有找到,则看amended是否为true,如为true,则说明,此map有追加,要寻找的key可能在dirty里面,于是就去dirty里面找,找到后,要把sync.Map的misses+1,意思是一次未命中。
※dirty 提升: 当sync.Map的misses>=len(dirty)的时候,就说明miss的几率过大,要把dirty提升为m,先连后删,让m的指针指向dirty指向的链表,dirty原先的指针置为nil,等有新的追加出现时,再重建dirty。此时,misses要重置为0,read的amended要改为false。进入一个新的轮回。
※删除:正常删除就是从m里寻找key对应的value,并把key指向value的指针置空为nil
※追加后删除:和正常删除一样,无非就是要去dirty里找一下key
※追加后提升,提升后删除:提升到m以后,被删除的值就是nil会被修改为expunged,在重建dirty的时候,将会判断key是否指向了expunged,如果是,则不重建该key
课程收获:
sync.map能支持并发的读写,但是适合读多写少的场景,如果写非常频繁,则退化到和加锁的普通map一样的效果。
共同学习,写下你的评论
评论加载中...
作者其他优质文章