一文带你深入探究Go语言中的sync.Map_第1页
一文带你深入探究Go语言中的sync.Map_第2页
一文带你深入探究Go语言中的sync.Map_第3页
一文带你深入探究Go语言中的sync.Map_第4页
一文带你深入探究Go语言中的sync.Map_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第一文带你深入探究Go语言中的sync.Map目录1.Map的基本实现原理2.sync.Map的实现原理2.1sync.Map的结构体定义2.2sync.Map的读取实现2.3sync.Map的写入实现2.4sync.Map的删除实现2.5sync.Map的遍历实现在Go语言中,有一个非常实用的并发安全的Map实现:sync.Map,它是在Go1.9版本中引入的。相比于标准库中的map,它的最大特点就是可以在并发情况下安全地读写,而不需要加锁。在这篇博客中,我们将深入探讨sync.Map的基本原理,帮助读者更好地理解并使用这个并发安全的Map。

1.Map的基本实现原理

在介绍sync.Map的基本实现原理之前,我们需要先了解一下Go语言标准库中的map实现原理。在Go中,map是基于哈希表实现的。当我们向map中添加元素时,它会根据key计算出一个哈希值,然后将这个值映射到一个桶中。如果该桶中已经有了元素,它会遍历桶中的元素,查找是否已经存在相同的key,如果存在就更新对应的值,否则就添加一个新的键值对。

下面是一个简单的map示例:

m:=make(map[string]int)

m["a"]=1

m["b"]=2

fmt.Println(m["a"])//Output:1

当我们运行这段代码时,Go语言会自动帮我们分配一个哈希表和若干个桶,然后将键值对添加到对应的桶中。这样,当我们需要访问某个key对应的值时,Go语言会根据哈希值快速定位到对应的桶,然后遍历桶中的元素,查找是否有相同的key,如果找到了就返回对应的值。

2.sync.Map的实现原理

sync.Map是Go语言标准库中的一个并发安全的Map实现,它可以在并发情况下安全地读写,而不需要加锁。那么,它是如何实现这种并发安全性的呢?下面我们就来一步步地解析sync.Map的实现原理。

2.1sync.Map的结构体定义

首先,让我们来看一下sync.Map的结构体定义:

typeMapstruct{

musync.Mutex

readatomic.Value//readOnly

dirtymap[interface{}]interface{}

missesint

dirtyLockeduintptr

}

从上面的代码中可以看出,sync.Map的实现主要是依赖于一个互斥锁(sync.Mutex)和两个map(read和dirty)。其中,read和dirty的作用分别是什么呢?我们先来看一下read的定义:

typereadOnlystruct{

mmap[interface{}]interface{}

amendedbool

}

可以看到,read只有一个成员m,它是一个map类型。而amended则表示read中的键值对是否被修改过。接下来,我们来看一下dirty的定义:

typedirtystruct{

mmap[interface{}]interface{}

dirtymap[interface{}]bool

missesint

}

和read不同的是,dirty中包含了两个map:m和dirty。其中,m存储了被修改过的键值对,而dirty则存储了哪些键值对被修改过。

2.2sync.Map的读取实现

在sync.Map中,读取操作非常简单,直接从readOnly中的m中查找即可。如果readOnly中的键值对被修改过,则需要从dirty中查找。读取操作的实现代码如下:

func(m*Map)Load(keyinterface{})(valueinterface{},okbool){

read,_:=m.read.Load().(readOnly)

value,ok=read.m[key]

if!okread.amended{

m.mu.Lock()

read,_=m.read.Load().(readOnly)

value,ok=read.m[key]

if!okread.amended{

value,ok=read.m[key]

m.mu.Unlock()

return

}

在这段代码中,我们首先从readOnly中的m中查找键值对。如果键值对不存在且readOnly中的键值对被修改过,则需要获取互斥锁,并重新从readOnly中查找。如果还是没有找到,那么就从dirty中查找。

2.3sync.Map的写入实现

在sync.Map中,写入操作需要分两步完成。首先,我们需要判断readOnly中的键值对是否被修改过,如果没有被修改过,则直接将键值对添加到readOnly中的m中即可。否则,我们需要获取互斥锁,然后将键值对添加到dirty中的m中,并将对应的键添加到dirty中的dirty中。写入操作的实现代码如下:

func(m*Map)Store(key,valueinterface{}){

read,_:=m.read.Load().(readOnly)

ifv,ok:=read.m[key];!ok!read.amended{

m.mu.Lock()

read,_=m.read.Load().(readOnly)

ifv,ok:=read.m[key];!ok{

read=readOnly{m:read.m,amended:true}

read.m[key]=value

m.read.Store(read)

m.mu.Unlock()

}else{

m.mu.Lock()

dirty:=m.dirtyLocked!=0

if!dirty{

m.dirtyLocked=1

m.dirty=make(map[interface{}]interface{})

m.dirty[key]=value

if!ok{

m.dirty[key]=value

m.dirty[key]=true

ifdirty{

m.mu.Unlock()

return

m.read.Store(readOnly{m:read.m,amended:true})

m.mu.Unlock()

}

在这段代码中,我们首先从readOnly中的m中查找键值对。如果键值对不存在且readOnly中的键值对没有被修改过,则需要获取互斥锁,并重新从readOnly中查找。如果还是没有找到,则将键值对添加到readOnly中的m中,并将amended设置为true。否则,我们需要获取互斥锁,并将键值对添加到dirty中的m中,并将对应的键添加到dirty中的dirty中。如果dirty中已经存在该键,则只需要更新dirty中的键值即可。如果dirty中没有该键,则需要在dirty中添加该键,并将该键的dirty置为true。

接下来,我们需要判断dirty是否被锁定。如果dirty被锁定,则直接退出函数。否则,我们需要将readOnly中的amended设置为true,并将readOnly存储回read中。

2.4sync.Map的删除实现

在sync.Map中,删除操作也需要分两步完成。首先,我们需要判断readOnly中的键值对是否被修改过,如果没有被修改过,则直接从readOnly中的m中删除键值对即可。否则,我们需要获取互斥锁,然后将键添加到dirty中的dirty中,并将dirty中的对应键的值设置为false。删除操作的实现代码如下:

func(m*Map)Delete(keyinterface{}){

read,_:=m.read.Load().(readOnly)

if_,ok:=read.m[key];ok||read.amended{

m.mu.Lock()

read,_=m.read.Load().(readOnly)

if_,ok:=read.m[key];ok||read.amended{

ifm.dirty==nil{

m.dirty=make(map[interface{}]interface{})

m.dirty[key]=false

m.dirty[key]=true

m.read.Store(readOnly{m:read.m,amended:true})

m.mu.Unlock()

}

在这段代码中,我们首先从readOnly中的m中查找键值对。如果键值对存在或者readOnly中的键值对被修改过,则需要获取互斥锁,并重新从readOnly中查找。如果还是没有找到,则将键添加到dirty中的dirty中,并将dirty中的对应键的值设置为false。接下来,我们需要判断dirty是否为nil,如果为nil,则需要将dirty初始化为一个空map。然后,我们将键添加到dirty中,并将dirty中的对应键的值设置为true。最后,我们将readOnly中的amended设置为true,并将readOnly存储回read中。

2.5sync.Map的遍历实现

在sync.Map中,遍历操作需要将readOnly和dirty中的所有键值对进行合并,并返回所有未被删除的键值对。遍历操作的实现代码如下:

func(m*Map)Range(ffunc(key,valueinterface{})bool){

read,_:=m.read.Load().(readOnly)

ifread.amended{

m.mu.Lock()

read,_=m.read.Load().(readOnly)

ifread.amended{

read=readOnly{

m:merge(read.m,m.dirty),

read.amended=false

m.read.Store(read)

m.dirty=nil

m.mu.Unlock()

fork,v:=rangeread.m{

if!f(k,v){

break

funcmerge(m1,m2map[interface{}]interface{})map[interface{}]interface{}{

iflen(m1)==0len(m2)==0{

returnnil

iflen(m1)==0{

returnm2

iflen(m2)==0{

returnm1

m:=make(map[interface{}]interface{})

fork,v:=rangem1{

m[k]=v

fork,v:=rangem2{

if_,ok:=m[k];!ok||!v.(bool){

m[k]=v

returnm

}

在这段代码中,我们首先从readOnly中获取所有的键值对,并检查是否有键值对被修改过。如果键值对被修改过,则需要获取互斥锁,并将readOnly和dirty中的键值对合并,然后将合并后的键值对存储回readOnly中,并将dirty设置为nil。接下来,我们遍历readOnly中的所有键值对,并调用f函数来处理键值对。如果f函数返回false,则遍历过程结束

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论