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) 

常用操作举例

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

声明

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

var myMapName sync.Map

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

增加元素

var myMapName sync.Map      //声明一个student Map元素为 <name, age>

myMapName.Store("xiaoming", "6")
myMapName.Store("xiaoqiang", "7")
myMapName.Store("xiaohei", "7")
fmt.Println(myMapName)

查找元素

查找返回两个结果,分别是 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")
}

遍历

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)

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

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)

删除元素

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

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

完整示例代码

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

package myMapTest

import (
	"fmt"
	"reflect"
	"strconv"
	"sync"
)

var myMapName sync.Map //声明一个student Map元素为 <name, age>
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*********************")
}

运行结果如下:

*********************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*********************

留待继续学习

//声明一个student Map元素为 <name, age>
var myMapName sync.Map   

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

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

最疑惑的是,明明已经执行了 Delete() 操作,而且再次查询 Key 值返回的也是 <nil> 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
}

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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值