golang 从 map 获取值时的值拷贝问题

Python019

golang 从 map 获取值时的值拷贝问题,第1张

我们知道 golang 中,slice, map, channel 是引用类型,函数之间传递都是以值拷贝的形式进行的,引用类型经过函数传递,依然是引用类型。

在上述例子中,我们从 map 中想拿出一个值,这个值是一个简单结构体,拿出这个值后,不确定这个值和 map 中的值是什么关系,如果不小心修改,是否会造成 map 值变更。

我们希望 golang 中更多的是值传递,这样能避免数据存储在堆上,造成 gc 负担。

可以看到,修改值后,map 中的值保持不变。说明 map 获取的值也是值传递出来的。

sync.Map是1.9才推荐的并发安全的map,除了互斥量以外,还运用了原子操作,所以在这之前,有必要了解下 Go语言——原子操作

go1.10\src\sync\map.go

entry分为三种情况:

从read中读取key,如果key存在就tryStore。

注意这里开始需要加锁,因为需要操作dirty。

条目在read中,首先取消标记,然后将条目保存到dirty里。(因为标记的数据不在dirty里)

最后原子保存value到条目里面,这里注意read和dirty都有条目。

总结一下Store:

这里可以看到dirty保存了数据的修改,除非可以直接原子更新read,继续保持read clean。

有了之前的经验,可以猜测下load流程:

与猜测的 区别

由于数据保存两份,所以删除考虑:

先看第二种情况。加锁直接删除dirty数据。思考下貌似没什么问题,本身就是脏数据。

第一种和第三种情况唯一的区别就是条目是否被标记。标记代表删除,所以直接返回。否则CAS操作置为nil。这里总感觉少点什么,因为条目其实还是存在的,虽然指针nil。

看了一圈貌似没找到标记的逻辑,因为删除只是将他变成nil。

之前以为这个逻辑就是简单的将为标记的条目拷贝给dirty,现在看来大有文章。

p == nil,说明条目已经被delete了,CAS将他置为标记删除。然后这个条目就不会保存在dirty里面。

这里其实就跟miss逻辑串起来了,因为miss达到阈值之后,dirty会全量变成read,也就是说标记删除在这一步最终删除。这个还是很巧妙的。

真正的删除逻辑:

很绕。。。。