面试题答案
一键面试使用 sync.Map
解决高并发数据竞争问题
- 基本使用:
sync.Map
是Go语言标准库提供的线程安全的map。无需像普通map那样手动加锁,它内部已经实现了锁机制来处理并发读写。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var m sync.Map
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key-%d", id)
value := fmt.Sprintf("value-%d", id)
m.Store(key, value)
}(i)
}
go func() {
wg.Wait()
m.Range(func(key, value interface{}) bool {
fmt.Printf("Key: %v, Value: %v\n", key, value)
return true
})
}()
select {}
}
在上述代码中,多个协程并发地向 sync.Map
中存储数据,sync.Map
能保证数据的一致性,不会出现数据竞争。
sync.Map
相较于普通Map在性能方面的优势
- 并发安全:普通map在高并发读写时需要手动加锁,而
sync.Map
内部实现了锁机制,无需手动加锁,使用起来更方便,也减少了因锁使用不当导致的死锁等问题。 - 自动清理:
sync.Map
采用了一种类似读写分离的结构,在删除元素时,并不会立即从数据结构中移除,而是标记为已删除。后续在适当的时候(如垃圾回收期间)自动清理,这在一定程度上提高了删除操作的性能。
sync.Map
相较于普通Map在性能方面的劣势
- 空间开销:
sync.Map
内部实现较为复杂,相比普通map需要更多的内存空间来存储元数据等信息,以实现并发安全和自动清理等功能。 - 性能特性:对于非高并发场景,普通map的性能通常优于
sync.Map
,因为普通map没有额外的锁开销和复杂的内部机制。而且在遍历sync.Map
时,由于其内部结构和自动清理机制,遍历性能可能不如普通map在加锁情况下的遍历性能。