34

go sync.map实现

 4 years ago
source link: https://www.tuicool.com/articles/yMviEr7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

golang map是非goroutine安全,如果多个goroutine使用map需要加锁。go 1.9之后提供了线程安全:sync.map。sync.map引入了两个数据结构来存储,他们的底层都是用map来实现。

Golang选择了 CAS 这种不用加锁的方案来更新值,实现并发安全的map。

下面会从源码说明一下他们的实现

type Map struct {
    mu Mutex
    read atomic.Value // readOnly
    dirty map[interface{}]*entry
    misses int
}

type readOnly struct {
    m       map[interface{}]*entry
    amended bool 
}

type entry struct {
    p unsafe.Pointer // *interface{}
}

read是readOnly结构体,真实数据存储在readOnly.m。

readOnly.amended表示read是否创建了dirty副本

read和dirty的关联:

RRVv6vQ.png!web

1: read相当于cache,读取数据时,先从read读取,没有取到,从dirty读取,Map.misses++。

当Map.misses达到一定值,把dirty里面的数据全部copy到read中,并且dirty置为nil。

2: dirty不为nil时,会保存全量的数据;dirty为nil时,read保存全量的数据

3: read和dirty map存储的元素值是放在entry结构体中。read和dirty中相同key值指向同一个entry地址,所以当对read的key对应的value值进行修改,dirty中的值也会相应的被修改。

entry.p 的状态:

1: nil表示entry被删除,并且Map.dirty = nil
2: expunged(初始化的entry.p)表示entry被删除,但是Map.dirty != nil
3: 其他情况表示值存在

snyc.Map主要提供了插入,查找,删除操作,接下来会主要会讲这三个方法的实现

插入流程

插入key, value

1: 先从read中获取key,如果存在,并且这个key没有被删除,则直接更新read[key] = entry{p: value}返回

2: 否则,key存在但是被删除了,在dirty中插入这个key,value值。dirty[key] = entry{p: value}返回

3: 如果dirty为nil,则将read map的key,entry 添加到新创建的dirty map中;不为nil,则跳过第3步

4: 将key, value插入dirty map中。dirty[key] = entry{p: value}

插入总结:

新加入的key值,会插入dirty中

以前存在,但是删除过的key,会插入dirty中

以前存在,但是没被删除的key,read会更新这个key对应的value值,

所以 dirty不为nil的时候,会全量保存key值。

查找流程

查找key

1: 从read中读取到,直接返回

2: 没有读取到,并且dirty不为nil,对map加锁,然后再读取一遍read map中内容(主要防止在加锁的过程中,有可能dirty map全部copy到read map,dirty置为nil),如果read存在key,直接返回

3: read不存在,从dirty中读取key对应的value值返回,并且map.misses++。当map.misses达到一定dirty长度,将dirty map全部copy到read map,dirty置为nil。

查找总结:

读read没读到,会从dirty中读取,并且misses 次数+1,当次数达到一定dirty长度,会把dirty map全部copy到read map,dirty置为nil。

删除流程

1: 从read中去读key,如果存在,直接将从read[key]获取到entry.p 置为nil

2: 否则,从dirty中删除这个key值

所以可以得出,read删除是直接把entry的p置为nil,key保留。从dirty中删除是直接删除这个key


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK