• GO sync.Map Store、Delete 、Load 、Range等方法使用举例


    概述

    Go 语言中 map 如果在并发 read 的情况下是线程安全的,如果是在并发write的情况下,则是线程不安全的。而 Golang 提供的 sync.Map 是并发readwrite都是安全的。

    sync.Map 则是一种并发安全的 map,在 Go 1.9 被引入:

    sync.Map 是线程安全的,读取,插入,删除也都保持着常数级的时间复杂度
    sync.Map 的零值是有效的,并且零值是一个空的 map。在第一次使用之后,不允许被拷贝。

    函数原型

    一般称存入 sync.Map 元素为键值对。其中存入键为 key,值为 value 的。sync.Map中的 key 和 value 都是 interface 类型的,因此 key 和 value 可以存入任意的类型。

    sync.Map 常用的函数原型如下:

    //Store sets the value for a key.
    func (m *Map) Store(key, value interface{}) 
    
    // Load returns the value stored in the map for a key, or nil if no
    // value is present.
    // The ok result indicates whether value was found in the map.
    func (m *Map) Load(key interface{}) (value interface{}, ok bool) 
    
    // Delete deletes the value for a key.
    func (m *Map) Delete(key interface{})
    
    // Range calls f sequentially for each key and value present in the map.
    // If f returns false, range stops the iteration.
    //
    // Range does not necessarily correspond to any consistent snapshot of the Map's
    // contents: no key will be visited more than once, but if the value for any key
    // is stored or deleted concurrently, Range may reflect any mapping for that key
    // from any point during the Range call.
    //
    // Range may be O(N) with the number of elements in the map even if f returns
    // false after a constant number of calls.
    func (m *Map) Range(f func(key, value interface{}) bool)
    
    // LoadOrStore returns the existing value for the key if present.
    // Otherwise, it stores and returns the given value.
    // The loaded result is true if the value was loaded, false if stored.
    func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) 
    
    // LoadAndDelete deletes the value for a key, returning the previous value if any.
    // The loaded result reports whether the key was present.
    func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    常用操作举例

    对开发而言,平时主要用到的操作,还是sync.Map 的增加(Store)、删除(Delete )、查找(Load )、遍历(Range )等方法。下面简要举例说明。

    声明

    声明一个变量名为 myMapName 的 sync.Map。

    var myMapName sync.Map
    
    • 1

    注:Go 语言 sync.Map 无须初始化,直接声明即可使用。

    增加元素

    var myMapName sync.Map      //声明一个student Map元素为 
    
    myMapName.Store("xiaoming", "6")
    myMapName.Store("xiaoqiang", "7")
    myMapName.Store("xiaohei", "7")
    fmt.Println(myMapName)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查找元素

    查找返回两个结果,分别是 value 值和状态(true 或者 false),因为返回的 valueinterface 类型的,因此 value 我们不可以直接使用,而必须要转换之后才可以使用,返回的 bool 值,表明获取是否成功。

    针对 sync.Map 通过 Load 获取不存在的元素时,返回 nilfalse

    承接上面已经添加的元素,查找方式如下

    myMapValue, isFind := myMapName.Load("xiaoming")
    if isFind {
    	fmt.Println("myMapValue=", myMapValue)
    } else {
    	fmt.Println("Failed to find xiaoming")
    }
    
    myMapValue, isFind = myMapName.Load("xiaoqiang")
    if isFind {
    	fmt.Println("myMapValue=", myMapValue)
    } else {
    	fmt.Println("Failed to find xiaoqiang")
    }
    
    myMapValue, isFind = myMapName.Load("xiao")
    if isFind {
    	fmt.Println("myMapValue=", myMapValue)
    } else {
    	fmt.Println("Failed to find xiao")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    遍历

    sync.Map 的元素遍历,不能使用 GOLang 中for 循环 或者 for range 循环,要使用 Range 方法并配合一个回调函数进行遍历操作。通过回调函数返回遍历出来的键值对(即 key 和 value )。

    • 当需要继续迭代遍历时,Range 参数中回调函数的返回 true
    • 当需要终止迭代遍历时,Range 参数中回调函数的返回 false

    具体示例如下:

    var NameSlice []string
    
    //funtion for sync.Map Range Callback
    func StudentList(name, age interface{}) bool {
    	nameStr := fmt.Sprintf("%v", name)
    	NameSlice = append(NameSlice, nameStr)
    
    	if len(NameSlice) == 0 {
    		return false
    	}
    	return true
    }
    
    myMapName.Range(StudentList)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    也可以采用另外简写的方式

    var MyName string
    var MyValue uint64
    
    myMapName.Range(func(key, value interface{}) bool {
    	//fmt.Println("key is ", key, "value is ", value)
    	MyName = fmt.Sprintf("%v", key)
    	MyValue, _ = strconv.ParseUint(fmt.Sprintf("%v", value), 10, 32)
    	return true
    })
    fmt.Println("key is", MyName, "value is", MyValue)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    删除元素

    myMapName.Delete("xiaoming")
    myMapName.Delete("xiaoqiang")
    fmt.Println(myMapName)               //打印当前myMapName 元素(key:value)
    
    • 1
    • 2
    • 3

    注意:删除并非直接删除 sync.Map 中的键值对(即键为 key,值为 value ),而是标记为 dirty

    完整示例代码

    以上例子的完整代码如下:

    package myMapTest
    
    import (
    	"fmt"
    	"reflect"
    	"strconv"
    	"sync"
    )
    
    var myMapName sync.Map //声明一个student Map元素为 
    var NameSlice []string
    
    //funtion for sync.Map Range Callback
    func StudentList(name, age interface{}) bool {
    	nameStr := fmt.Sprintf("%v", name)
    	NameSlice = append(NameSlice, nameStr)
    
    	if len(NameSlice) == 0 {
    		return false
    	}
    	return true
    }
    
    func TestMyMapName() {
    	fmt.Println("*********************Test myMapValue Start*********************")
    
    	fmt.Println("Test myMapValue Store() function")
    	myMapName.Store("xiaoming", "6")
    	myMapName.Store("xiaoqiang", "7")
    	myMapName.Store("xiaohei", "7")
    	fmt.Println(myMapName)
    
    	//load
    	fmt.Println("Test myMapValue Load() function")
    	myMapValue, isFind := myMapName.Load("xiaoming")
    	if isFind {
    		fmt.Println("myMapValue=", myMapValue)
    	} else {
    		fmt.Println("Failed to find xiaoming")
    	}
    
    	myMapValue, isFind = myMapName.Load("xiaoqiang")
    	if isFind {
    		fmt.Println("myMapValue=", myMapValue)
    	} else {
    		fmt.Println("Failed to find xiaoqiang")
    	}
    
    	myMapValue, isFind = myMapName.Load("xiao")
    	if isFind {
    		fmt.Println("myMapValue=", myMapValue)
    	} else {
    		fmt.Println("Failed to find xiaohei")
    	}
    
    	//TypeOf
    	fmt.Println("Test myMapValue TypeOf() function")
    	fmt.Println("TypeOf(myMapName) is ", reflect.TypeOf(myMapName))
    
    	//Range-1
    	fmt.Println("Test myMapValue Range() function - 1")
    	var MyName string
    	var MyValue uint64
    	NameSlice = NameSlice[0:0]
    	myMapName.Range(func(key, value interface{}) bool {
    		//fmt.Println("key is", key, "value is", value)
    		MyName = fmt.Sprintf("%v", key)
    		NameSlice = append(NameSlice, MyName)
    		MyValue, _ = strconv.ParseUint(fmt.Sprintf("%v", value), 10, 32)
    		return true
    	})
    	fmt.Println("key is", MyName, "value is", MyValue)
    
    	fmt.Println("Test myMapValue Range() function - 2")
    	//Range-2
    	NameSlice = NameSlice[0:0]
    	myMapName.Range(StudentList)
    	fmt.Println("Student NameSlice is", NameSlice)
    
    	//Delete
    	fmt.Println("Test myMapValue Delete() function")
    	fmt.Println(myMapName)
    
    	myMapName.Delete("xiaoming")
    	myMapName.Delete("xiaoqiang")
    	fmt.Println(myMapName.Load("xiaoming"))
    	fmt.Println(myMapName.Load("xiaoqiang"))
    
    	fmt.Println("Test myMapValue Print myMapName")
    	fmt.Println(myMapName)
    
    	fmt.Println("*********************Test myMapValue End*********************")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    运行结果如下:

    *********************Test myMapValue Start*********************
    Test myMapValue Store() function
    {{0 0} {{map[] true}} map[xiaohei:0xc000010278 xiaoming:0xc000010250 xiaoqiang:0xc000010260] 0}
    Test myMapValue Load() function
    myMapValue= 6
    myMapValue= 7
    Failed to find xiaohei
    Test myMapValue TypeOf() function
    TypeOf(myMapName) is  sync.Map
    Test myMapValue Range() function - 1
    key is xiaohei value is 7
    Test myMapValue Range() function - 2
    Student NameSlice is [xiaoqiang xiaohei xiaoming]
    Test myMapValue Delete() function
    {{0 0} {{map[xiaohei:0xc000010278 xiaoming:0xc000010250 xiaoqiang:0xc000010260] false}} map[] 0}
    <nil> false
    <nil> false
    Test myMapValue Print myMapName
    {{0 0} {{map[xiaohei:0xc000010278 xiaoming:0xc000010250 xiaoqiang:0xc000010260] false}} map[] 0}
    *********************Test myMapValue End*********************
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    留待继续学习

    //声明一个student Map元素为 
    var myMapName sync.Map   
    
    • 1
    • 2

    基于声明的 myMapName ,针对增加(Store)、删除(Delete )、查找(Load )、遍历(Range )等使用方法进行了尝试。但是在完整代码测试的最后一行,本想打印的是整个 myMapName 中的数值,对比最初的打印,存在较为明显的不同。

    {{0 0} {{map[] true}} map[xiaohei:0xc000010278 xiaoming:0xc000010250 xiaoqiang:0xc000010260] 0}
    
    • 1
    {{0 0} {{map[xiaohei:0xc000010278 xiaoming:0xc000010250 xiaoqiang:0xc000010260] false}} map[] 0}
    
    • 1

    最疑惑的是,明明已经执行了 Delete() 操作,而且再次查询 Key 值返回的也是 false,但是为何打印整个 myMapName 却显示的仍然是 Store() 后的所有数据?这就需要继续针对 sync.Map 设计原理和源码进行继续的分析和学习了。

    // The zero Map is empty and ready for use. A Map must not be copied after first use.
    type Map struct {
    	mu Mutex
    	read atomic.Value // readOnly
    	dirty map[interface{}]*entry
    	misses int
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Reference

    https://golang.google.cn/pkg/
    Package sync - type Map 1.9
    https://halfrost.com/go_map_chapter_one/
    https://halfrost.com/go_map_chapter_two/

  • 相关阅读:
    登峰造极,师出造化,Pytorch人工智能AI图像增强框架ControlNet绘画实践,基于Python3.10
    《向量数据库指南》——TruLens 用于语言模型应用跟踪和评估
    C++二叉树
    无法与自己联结,在关系中终将走向孤立
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
    剖析C语言中的自定义类型(结构体、枚举常量、联合)兼内存对齐与位段
    基于.NetCore开发博客项目 StarBlog - (11) 实现访问统计
    localStorage设置过期时间用法
    2022年第十三届蓝桥杯大赛湖南中医药大学第2场选拔赛(部分总结)
    Java基础40 断点调试(Debug)
  • 原文地址:https://blog.csdn.net/lm_hao/article/details/126928751