概述
Go 语言中 map 如果在并发 read 的情况下是线程安全的,如果是在并发write的情况下,则是线程不安全的。而 Golang 提供的 sync.Map 是并发read和write都是安全的。
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),因为返回的 value
是 interface
类型的,因此 value 我们不可以直接使用,而必须要转换之后才可以使用,返回的 bool 值,表明获取是否成功。
针对 sync.Map 通过 Load 获取不存在的元素时,返回 nil
和 false
承接上面已经添加的元素,查找方式如下
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/