面试题答案
一键面试可能出现的问题
在Go语言中使用Map的懒加载模式且存在并发访问时,可能会出现以下问题:
- 数据竞争(Data Race):多个协程同时读写Map,可能导致数据不一致。例如,一个协程正在读取Map中的值,另一个协程同时对Map进行写入操作,这可能会使读取操作得到部分更新或者未更新的数据。
解决方案
- 使用
sync.Mutex
:通过互斥锁来保护对Map的访问。在读取或写入Map前,先获取锁,操作完成后释放锁。 - 使用
sync.RWMutex
:如果读操作远多于写操作,可以使用读写锁。读操作时获取读锁,允许多个协程同时读取;写操作时获取写锁,此时其他读写操作都被阻塞。 - 使用
sync.Map
:Go 1.9引入的sync.Map
,它是线程安全的,适合高并发场景。它内部实现了更复杂的机制来处理并发访问,不需要用户手动加锁。
代码示例
- 使用
sync.Mutex
package main
import (
"fmt"
"sync"
)
var (
dataMap = make(map[string]int)
mu sync.Mutex
)
func lazyLoad(key string) int {
mu.Lock()
defer mu.Unlock()
if val, ok := dataMap[key]; ok {
return val
}
// 模拟加载数据
newVal := 42
dataMap[key] = newVal
return newVal
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key-%d", id)
result := lazyLoad(key)
fmt.Printf("协程 %d 加载结果: %d\n", id, result)
}(i)
}
wg.Wait()
}
- 使用
sync.RWMutex
package main
import (
"fmt"
"sync"
)
var (
dataMap = make(map[string]int)
rwmu sync.RWMutex
)
func lazyLoad(key string) int {
rwmu.RLock()
if val, ok := dataMap[key]; ok {
rwmu.RUnlock()
return val
}
rwmu.RUnlock()
rwmu.Lock()
defer rwmu.Unlock()
if val, ok := dataMap[key]; ok {
return val
}
// 模拟加载数据
newVal := 42
dataMap[key] = newVal
return newVal
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key-%d", id)
result := lazyLoad(key)
fmt.Printf("协程 %d 加载结果: %d\n", id, result)
}(i)
}
wg.Wait()
}
- 使用
sync.Map
package main
import (
"fmt"
"sync"
)
var dataMap sync.Map
func lazyLoad(key string) int {
if val, ok := dataMap.Load(key); ok {
return val.(int)
}
// 模拟加载数据
newVal := 42
dataMap.Store(key, newVal)
return newVal
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key-%d", id)
result := lazyLoad(key)
fmt.Printf("协程 %d 加载结果: %d\n", id, result)
}(i)
}
wg.Wait()
}