go sync.map的使用

Posted 牛奔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go sync.map的使用相关的知识,希望对你有一定的参考价值。

前言

数据竞争是并发情况下,存在多线程/协程读写相同数据的情况,必须存在至少一方写。另外,全是读的情况下是不存在数据竞争的。

Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。

如果map由多协程同时读和写就会出现 fatal error:concurrent map read and map write的错误。这是因为map在Go语言并发编程中,如果仅用于读取数据时候是安全的,但是在读写操作的时候是不安全的,在Go语言1.9版本后提供了一种并发安全的,sync.Map是Go语言提供的内置map,不同于基本的map数据类型,所以不能像操作基本map那样的方式操作数据,他提供了特有的方法,不需要初始化操作实现增删改查的操作。

需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是以语言原生形态提供,而是在 sync 包下的特殊结构。

sync.Map 特性

无须初始化,直接声明即可。
sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除。
使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。

sync.Map提供的常用方法有如下七个:

  • Load(key interface) (value interface,ok bool):通过参数key查询对应的value,如果不存在则返回nil;ok表示是否找到对应的值。
  • Store(key,value interface):该方法相当于对sync.Map的更新或新增,参数是键值对。
  • LoadOrStore(key,value interface) (actual interface,loaded bool):该方法的参数为key和value。该方法会先根据参数key查找对应的value,如果找到则不修改原来的值并通过actual返回,并且loaded为true;如果通过key无法查找到对应的value,则存储key-value并且将存储的value通过actual返回,loaded为false。
  • Delete(key interface):通过key删除键值对。
  • LoadAndDelete(key interface):通过key删除键的值,如果有,则返回上一个值。
  • Range(f func(key,value interface) bool):遍历sync.Map的元素,注意for...range map是对内置map类型的用法,sync.Map需要使用单独的Range方法。

并发安全的 sync.Map 演示代码如下:

package main

import (
    "fmt"
    "sync"
)

//声明sync.Map
var syncmap sync.Map

func main() 

    //Store方法将键值对保存到sync.Map
    syncmap.Store("zhangsan", 97)
    syncmap.Store("lisi", 100)
    syncmap.Store("wangmazi", 200)

    // LoadOrStore key不存在
    v, ok := syncmap.LoadOrStore(3, "three")
    fmt.Println(v, ok) // three false
    // LoadOrStore key存在
    v, ok = syncmap.LoadOrStore(1, "thisOne")
    fmt.Println(v, ok) // one ture

    // Load方法获取sync.Map 键所对应的值
    fmt.Println(syncmap.Load("lisi"))

    // Delete方法键删除对应的键值对
    syncmap.Delete("lisi")

    var syncmap sync.Map
    // LoadAndDelete key不存在
    v, ok := syncmap.LoadAndDelete("xiaomi")
    fmt.Println(v, ok) // <nil> false
    syncmap.Store("xiaomi", "xiaomi")
    // LoadAndDelete key存在
    v, ok = syncmap.LoadAndDelete("xiaomi")
    fmt.Println(v, ok) // xiaomi true

    // Range遍历所有sync.Map中的键值对
    syncmap.Range(func(k, v interface) bool 
        fmt.Println(k, v)
        return true
    )


注意

声明 score,类型为 sync.Map,注意,sync.Map 不能使用 make 创建。

将一系列键值对保存到 sync.Map 中,sync.Map 将键和值以 interface 类型进行保存。

Range() 方法可以遍历 sync.Map,遍历需要提供一个匿名函数,参数为 k、v,类型为 interface,每次 Range() 在遍历一个元素时,都会调用这个匿名函数把结果返回。

sync.Map 没有提供获取 map 数量的方法,替代方法是在获取 sync.Map 时遍历自行计算数量,sync.Map 为了保证并发安全有一些性能损失,因此在非并发情况下,使用 map 相比使用 sync.Map 会有更好的性能。

以上是关于go sync.map的使用的主要内容,如果未能解决你的问题,请参考以下文章

Go Map 为啥是非线程安全的?

Go36-34,35-并发安全字典(sync.Map)

go - 更为安全的使用 sync.Map 组件

Go sync.Map

深度解密 Go 语言之 sync.map

sync.Map源码分析