网站和软件建站,知名网站有哪些,wordpress时间代码,山西公司怎么做网站文章目录 Go中map使用以及注意事项map使用时的并发安全问题 Go中map使用以及注意事项
Go语言中map使用简单示例#xff1a;
func main() {var mp map[string]int// mp : map[string]int{}val, ok : mp[one]if ok {fmt.Println(val)} else {fmt.Println(val)}mp[… 文章目录 Go中map使用以及注意事项map使用时的并发安全问题 Go中map使用以及注意事项
Go语言中map使用简单示例
func main() {var mp map[string]int// mp : map[string]int{}val, ok : mp[one]if ok {fmt.Println(val)} else {fmt.Println(val)}mp[two] 10
}思考一Go语言的map的键类型为什么不能是函数类型、字典类型、切片类型 相信学习过其他语言比如Java语言的朋友对于Java中的HashMap都比较熟悉对于所有键值对元素的数据结构来说存储过程先计算key的哈希值随后使用哈希值定位到对应的bucket将key和value作为元素存储到对应的bucket中。在这个过程中可能会发生哈希碰撞在Java中对于哈希碰撞常见的解决方案有拉链法由于哈希碰撞的存在对于哈希表的键的要求不仅仅能够计算哈希值同时能够对键进行判等操作解决哈希碰撞时key重复的问题。对于Go语言的map的实现仍然如此需要考虑到哈希碰撞的出现所以就要求key必须能够进行判等操作。 如果go的map的键为上述三种类型在运行时就会发生panic。 思考二Go语言中应该优先考虑那些类型作为map的键的类型 只从性能的角度出发而不考虑上下文时在map中比较耗时的操作主要出现在连个地方 把键值转换为哈希值把要查找的键值与哈希桶中的键值做对比 对于所有的基本类型、指针类型以及数组类型、结构体类型和接口类型Go 语言都有一套算法与之对应。这套算法中就包含了哈希和判等。 以求哈希的操作为例宽度越小的类型速度通常越快。对于布尔类型、整数类型、浮点数类型、复数类型和指针类型来说都是如此。对于字符串类型由于它的宽度是不定的所以要看它的值的具体长度长度越短求哈希越快。类型的宽度是指它的单个值需要占用的字节数。比如bool、int8和uint8类型的一个值需要占用的字节数都是1因此这些类型的宽度就都是1。高级类型如数组类型的值求哈希实际上是依次求得它的每个元素的哈希值并进行合并所以速度就取决于它的元素类型以及它的长度。细则同上。结构体类型哈希实际上就是对它的所有字段值求哈希并进行合并关键在于它的各个字段的类型以及字段的数量。 优先选用数值类型和指针类型通常情况下类型的宽度越小越好。如果非要选择字符串类型的话最好对键值的长度进行额外的约束。 map使用时的并发安全问题
考虑如下代码
func main() {m : map[int]string{1: haha,}go read(m)time.Sleep(time.Second)go write(m)time.Sleep(30 * time.Second)fmt.Println(m)
}
func read(m map[int]string) {for {_ m[1]time.Sleep(1)}
}
func write(m map[int]string) {for {m[1] writetime.Sleep(1)}
}开启两个协程并发对map进行读写操作上述代码运行直接报如下错误
goroutine 6 [running]:
main.read(0x0?)/home/wt/Backend/go/goprojects/src/golearndetail/go36/learn09/demo02.go:20 0x2d
created by main.main/home/wt/Backend/go/goprojects/src/golearndetail/go36/learn09/demo02.go:12 0xa5goroutine 1 [sleep]:
time.Sleep(0x6fc23ac00)/usr/local/go/src/runtime/time.go:195 0x135
main.main()/home/wt/Backend/go/goprojects/src/golearndetail/go36/learn09/demo02.go:15 0xfbgoroutine 7 [sleep]:
time.Sleep(0x1)/usr/local/go/src/runtime/time.go:195 0x135
main.write(0x0?)/home/wt/Backend/go/goprojects/src/golearndetail/go36/learn09/demo02.go:27 0x25
created by main.main/home/wt/Backend/go/goprojects/src/golearndetail/go36/learn09/demo02.go:14 0xecProcess finished with the exit code 2对于map的读写是非原子性操作存在资源竞争不是现成安全的可以使用如下命令检测
go run race ...为了将非并发安全的读取操作更改为并发安全的可以引入sync.Mutex在读、写操作前进行加锁操作操作后进行解锁保证并发安全。
func main() {m : map[int]string{1: haha,}var mutex sync.Mutex // 创建一个互斥锁// 启动读协程go read(m, mutex)// 等待一秒钟确保读协程已经开始运行time.Sleep(time.Second)// 启动写协程go write(m, mutex)// 等待足够长的时间以便读写协程可以运行time.Sleep(30 * time.Second)// 打印最终的mapfmt.Println(m)
}func read(m map[int]string, mutex *sync.Mutex) {for {mutex.Lock() // 在读取之前加锁value : m[1]mutex.Unlock() // 读取完毕后解锁fmt.Println(Read:, value)time.Sleep(1 * time.Second)}
}func write(m map[int]string, mutex *sync.Mutex) {for {mutex.Lock() // 在写入之前加锁m[1] writemutex.Unlock() // 写入完毕后解锁fmt.Println(Write:, m[1])time.Sleep(1 * time.Second)}
}后续会将sync.Mutex的底层原理进行总结展示。