灰子的Go笔记:sync.Map
背景介绍:
在golang中map不是并发安全的,所有才有了sync.Map的实现,尽管sync.Map的引入确实从性能上面解决了map的并发安全问题,不过sync.Map却没有实现len()函数,这导致了在使用sync.Map的时候,一旦需要计算长度,就比较麻烦,一定要在Range函数中去计算长度(备注:这个后面会有例子给出)。
基于上面的现状,笔者从下面几点开始整理了这些知识:
- 普通map的介绍,包括线程不安全部分和常规使用方法。
- sync.Map的使用方式。
- sync.Map的底层实现介绍。
一、map的介绍
- map的并发不安全 例子如下: package main import ( "fmt" "sync" ) func main() { test := map[int]int{} var wg sync.WaitGroup i := 0 for i < 1000 { wg.Add(1) go func() { defer wg.Done() test[1] = i }() i++ } wg.Wait() fmt.Println(test) }
输出结果: 通过运行结果我们可以看出map是并发不安全的。
2. map的常规使用
基于上面的原因,我们在工程里面使用map操作的时候,通常会添加互斥锁或者读写锁来解决map的并发不安全问题。
例子如下:
package main
import (
"fmt"
"sync"
)
func main() {
test := map[int]int{}
var wg sync.WaitGroup
var l sync.Mutex
i := 0
for i < 1000 {
wg.Add(1)
go func() {
defer wg.Done()
l.Lock()
test[1] = i
l.Unlock()
}()
i++
}
wg.Wait()
fmt.Println(test)
}
输出结果:添加互斥锁之后,map便可以解决并发冲突问题。
map[1:1000]
不好之处:
同样的道理读写锁也可以达到上面的效果,虽然这样map可以在工程里面使用,但是效果不好,毕竟加锁的话,就是对整个map进行加锁和解锁,导致整个map在加锁的过程中都被阻塞住,这种操作会严重影响性能。
二、sync.Map的使用
也正是因为上面的原因,golang官方提供了一个官方的sync.map来解决这个问题,具体api如下所示:
对于sync.Map来说,他并没有实现len()函数,不过他却提供了一个Range函数,用于我们来计算sync.Map的长度。
先看一个解决并发问题的例子:
package main
import (
"fmt"
"sync"
)
func main() {
test := sync.Map{}
var wg sync.WaitGroup
i := 0
for i < 1000 {
wg.Add(1)
go func() {
defer wg.Done()
test.Store(1,i)
}()
i++
}
wg.Wait()
fmt.Println(test.Load(1))
}
结果输出:
map[1:1000]
例子2, 计算sync.Map的长度
package main
import (
"fmt"
"sync"
)
func main() {
test := sync.Map{}
var wg sync.WaitGroup
i := 0
for i < 1000 {
wg.Add(1)
go func(i int) {
defer wg.Done()
test.LoadOrStore(i, 1)
}(i)
i++
}
wg.Wait()
len := 0
test.Range(func(k, v interface{}) bool {
len++
return true
})
fmt.Println("len of test:",len)
}
输出结果:
len of test: 1000
三、sync.Map的底层实现介绍
map的源代码可以参考下面的链接:https://golang.org/src/sync/map.go?s=1149:2596#L17
pe Map struct {
28 mu Mutex
29
30 // read contains the portion of the map's contents that are safe for
31 // concurrent access (with or without mu held).
32 //
33 // The read field itself is always safe to load, but must only be stored with
34 // mu held.
35 //
36 // Entries stored in read may be updated concurrently without mu, but updating
37 // a previously-expunged entry requires that the entry be copied to the dirty
38 // map and unexpunged with mu held.
39 read atomic.Value // readOnly
40
41 // dirty contains the portion of the map's contents that require mu to be
42 // held. To ensure that the dirty map can be promoted to the read map quickly,
43 // it also includes all of the non-expunged entries in the read map.
44 //
45 // Expunged entries are not stored in the dirty map. An expunged entry in the
46 // clean map must be unexpunged and added to the dirty map before a new value
47 // can be stored to it.
48 //
49 // If the dirty map is nil, the next write to the map will initialize it by
50 // making a shallow copy of the clean map, omitting stale entries.
51 dirty map[interface{}]*entry
52
53 // misses counts the number of loads since the read map was last updated that
54 // needed to lock mu to determine whether the key was present.
55 //
56 // Once enough misses have occurred to cover the cost of copying the dirty
57 // map, the dirty map will be promoted to the read map (in the unamended
58 // state) and the next store to the map will make a new dirty copy.
59 misses int
60 }type readOnly struct {
64 m map[interface{}]*entry
65 amended bool // true if the dirty map contains some key not in m.
66 }
对于sync.Map的英文描述如下所示:
// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
13 // by multiple goroutines without additional locking or coordination.
14 // Loads, stores, and deletes run in amortized constant time.
15 //
16 // The Map type is specialized. Most code should use a plain Go map instead,
17 // with separate locking or coordination, for better type safety and to make it
18 // easier to maintain other invariants along with the map content.
19 //
20 // The Map type is optimized for two common use cases: (1) when the entry for a given
21 // key is only ever written once but read many times, as in caches that only grow,
22 // or (2) when multiple goroutines read, write, and overwrite entries for disjoint
23 // sets of keys. In these two cases, use of a Map may significantly reduce lock
24 // contention compared to a Go map paired with a separate Mutex or RWMutex.
25 //
26 // The zero Map is empty and ready for use. A Map must not be copied after first
对于sync.Map有两个map构成,一个用于读,一个用于写。用于写的叫dirty,采用互斥锁进行加锁,对于只读的数据会先读提供读的map,然后才会去读dirty。
为了优化sync.Map的性能,还提供了一个missed计数,用于来决策何时将dirty中的元素变成只读的map元素等操作。
具体实现细节可以参考代码,或者 参考文档中的3.
参考文档:
1. https://golang.org/pkg/sync/#Map
2. https://blog.csdn.net/u011957758/article/details/82846609
3. 由浅入深聊聊Golang的sync.Map:
https://juejin.im/post/6844903895227957262
- 省市县三级联动
- 从国家统计局爬下来的地区信息
- app令牌的一个token实现
- velocity分页模板
- js基础-表单验证和提交
- No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?
- oracle创建用户
- oracle创建表相关
- spring学习遇到的问题汇总
- Java XML解析工具 dom4j介绍及使用实例
- redis学习教程之一基本命令
- 在java中使用redis
- springmvc学习笔记--json--返回json的日期格式问题
- springmvc学习笔记--mybatis--使用插件自动生成实体和mapper
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法