面试题答案
一键面试使用Map可能遇到的问题
- 并发读写问题:Go语言中的Map不是线程安全的,当多个goroutine同时对一个Map进行读写操作时,可能会导致数据竞争,进而出现未定义行为,比如程序崩溃或得到错误的结果。 例如:
package main
import (
"fmt"
)
func main() {
var m = make(map[string]int)
for i := 0; i < 10; i++ {
go func(n int) {
key := fmt.Sprintf("key%d", n)
m[key] = n
}(i)
}
fmt.Println(m)
}
上述代码在多个goroutine并发写Map时,可能会引发数据竞争,运行时可能会崩溃并提示类似 fatal error: concurrent map writes
的错误。
常见解决方案
- 使用sync.Mutex:通过互斥锁来保护对Map的读写操作,确保同一时间只有一个goroutine可以访问Map。 示例代码:
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
var m = make(map[string]int)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
key := fmt.Sprintf("key%d", n)
mu.Lock()
m[key] = n
mu.Unlock()
}(i)
}
wg.Wait()
mu.Lock()
fmt.Println(m)
mu.Unlock()
}
- 使用sync.RWMutex:如果读操作远多于写操作,可以使用读写互斥锁。它允许多个goroutine同时进行读操作,但写操作时会独占Map。 示例代码:
package main
import (
"fmt"
"sync"
)
func main() {
var rwmu sync.RWMutex
var m = make(map[string]int)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
key := fmt.Sprintf("key%d", n)
rwmu.Lock()
m[key] = n
rwmu.Unlock()
}(i)
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
rwmu.RLock()
fmt.Println(m)
rwmu.RUnlock()
}()
}
wg.Wait()
}
- 使用sync.Map:Go 1.9 引入了
sync.Map
,这是一个线程安全的Map实现。它适用于高并发场景,在不需要额外锁的情况下支持并发读写。 示例代码:
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
key := fmt.Sprintf("key%d", n)
m.Store(key, n)
}(i)
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
m.Range(func(key, value interface{}) bool {
fmt.Printf("key: %v, value: %v\n", key, value)
return true
})
}()
}
wg.Wait()
}