Go语言:sync.Map
Go语言的普通map由于不是线程安全的,所以很多时候也会使用sync包的Map来代替。sync.Map是线程安全的,但是也必须使用其提供的接口,接口不多,光看名字就知道其用途。先看下其中基本的结构
1. 数据结构
-
type Map struct {
-
mu Mutex // 内部互斥锁,增,改数据会用到,删除可能会用到
-
-
read atomic.Value // readOnly 包含部分数据,但是多线程读安全
-
-
dirty map[interface{}]*entry // 存放新增数据
-
-
misses int // 多次从read中读取失败会增加此计数,大于等于dirty后会出发missLocked,即拷贝全量dirty覆盖read
-
}
-
type readOnly struct {
-
m map[interface{}]*entry // 多线程读安全
-
amended bool // 为true时,表明dirty map中新插入的key-value,未同步到readonly
-
}
2.接口
- Load(key interface{}) (value interface{}, ok bool)
-
func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
-
read, _ := m.read.Load().(readOnly)
-
e, ok := read.m[key]
-
-
// 从read中读取失败,并且dirty有更新,则从dirty加锁再读取一次
-
if !ok && read.amended {
-
m.mu.Lock()
-
read, _ = m.read.Load().(readOnly)
-
e, ok = read.m[key]
-
if !ok && read.amended {
-
e, ok = m.dirty[key]
-
// 从read中read miss后会出发missLocked,记录miss次数,超过dirty大小,就会强制更新read
-
m.missLocked()
-
}
-
m.mu.Unlock()
-
}
-
if !ok {
-
return nil, false
-
}
-
// load 原子操作
-
return e.load()
-
}
- Store(key, value interface{})
-
func (m *Map) Store(key, value interface{}) {
-
// key存在于read中,即修改其值,tryStore是原子操作
-
read, _ := m.read.Load().(readOnly)
-
if e, ok := read.m[key]; ok && e.tryStore(&value) {
-
return
-
}
-
-
// 不存在read,或者原子修改失败即表明此时有其他线程同时在删除该key
-
// 加锁重新从read读取,不信邪?
-
m.mu.Lock()
-
read, _ = m.read.Load().(readOnly)
-
if e, ok := read.m[key]; ok {
-
// update
-
if e.unexpungeLocked() {
-
// 如果已被清除,则直接修改dirty
-
m.dirty[key] = e
-
}
-
// 再修改read
-
e.storeLocked(&value)
-
} else if e, ok := m.dirty[key]; ok {
-
// 如果该key不在read而在dirty中,则直接修改dirty
-
// update
-
e.storeLocked(&value)
-
} else {
-
// insert
-
// amended为false,表示read是全量数据,还未进行任何修改
-
// amended为true,表示dirty有新增key-value
-
if !read.amended {
-
// dirtylock
-
// 1、 dirty map的初始化
-
// 2、并且将read中的值更新到dirty
-
// 3、将删除的key置为expunged
-
m.dirtyLocked()
-
// 单纯修改amended变量,
-
m.read.Store(readOnly{m: read.m, amended: true})
-
}
-
// dirty插入新值
-
m.dirty[key] = newEntry(value)
-
}
-
m.mu.Unlock()
-
}
-
Delete(key interface{})
-
func (m *Map) Delete(key interface{}) {
-
read, _ := m.read.Load().(readOnly)
-
e, ok := read.m[key]
-
// read中不存在,amended为true表示有新值 那么就去dirty看看
-
if !ok && read.amended {
-
m.mu.Lock()
-
// 不信邪 再读一次
-
read, _ = m.read.Load().(readOnly)
-
e, ok = read.m[key]
-
if !ok && read.amended {
-
// read中还是没有,就直接删除dirty map吧,有没有已经不重要了,反正加锁了
-
delete(m.dirty, key)
-
}
-
m.mu.Unlock()
-
}
-
if ok {
-
// 并没有真正删除,只是把key对应value置为nil
-
e.delete()
-
}
-
}
3. 想到几个问题
- 内部分为两个map,read和dirty,读写分离,读取时原子操作;即使在读取时有删除操作也不影响;
- 更改和插入数据时,在内部会加锁;
- 如果该key存在read,则直接置为nil,不管dirty,否则直接从dirty删除(删除key和value);
- 将来触发dirtyLocked时(即插入新key-value),即从read更新dirty时,将value为nil的键对应的值修改expunged;
- 将来触发missLocked(即多次从read读取失败,必须去dirty查找的次数)时,将dirty直接拷贝覆盖read,这时才会真正释放删除的key-value;
步骤1 释放value,步骤3释放key,如果key是一些较大或重要的内存的引用,那么就可能要很久才能释放key对应的内存
- 如果该key在read中,则会直接尝试原子操作修改read中key-value;
- 否则就要加锁进行判断,去dirty中修改了;
- 内部是读写分离,所以只读不写不改那就最好了,内部都是原子操作,贼快;
- 如果用sync.Map频繁读取一些不存在的键,但是修改比较少的话也贼快
- 内部存储的是value对应的指针,删除的时候,将read中对应value置为nil,但是这并妨碍我们使用value来存nil
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgcbjja
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01