Go语言的字典类型Map、以及并发安全的sync.Map类型(map)

Go语言的字典类型Map、以及并发安全的sync.Map类型(map)

Go语言的map类型底层使用Hash表实现;(无序集合);

map类型的数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 源码来自Go 1.19
// 源码包:/usr/local/go/src/runtime/map.go
type hmap struct {
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed

buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)

extra *mapextra // optional fields
}

map类型的增、删、改、查

1
2
3
4
5
6
7
8
9
10
11
func mapCURD() {
m := make(map[string]string, 10)

m["apple"] = "red" // 添加
m["apple"] = "green" // 修改
delete(m, "apple") // 删除
val, exist := m["apple"] // 查询
if exist {
fmt.Println("apple - %s\n", val)
}
}

sync.Map并发安全的字典类型

1
2
3
4
5
6
type Map struct {
mu Mutex
read atomic.Value // 只负责读数据
dirty map[interface{}]*entey
misses int
}

sync.Map的主要思想就是:读写分离,空间换时间

1.(read)字段负责只读数据,并发安全(atomic.Value),避免读写冲突;

2.动态调整,miss次数多了之后,将dirty数据迁移到read中;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Map struct {
// 当涉及到脏数据(dirty)操作时候,需要使用这个锁
mu Mutex

// read是一个只读数据结构,包含一个map结构,
// 读不需要加锁,只需要通过 atomic 加载最新的数据即可
read atomic.Value // readOnly

// dirty 包含部分map的键值对,如果操作需要mutex获取锁
// 最后dirty中的元素会被全部提升到read里的map去
dirty map[interface{}]*entry

// misses是一个计数器,用于记录read中没有的数据而在dirty中有的数据的数量
// 也就是说如果read不包含这个数据,会从dirty中读取,并misses+1
// 当misses的数量等于dirty的长度,就会将dirty中的数据迁移到read中
misses int
}