面试题答案
一键面试可能出现的问题
在Go语言中,map不是线程安全的。当多个goroutine同时对map进行读写操作,尤其是包含删除操作时,可能会出现以下问题:
- 数据竞争:这会导致未定义行为,程序可能崩溃或者产生不可预期的结果。
解决方案及示例代码
- 使用sync.Mutex
- 原理:通过互斥锁(Mutex)来保证同一时间只有一个goroutine可以对map进行读写或删除操作,从而避免数据竞争。
- 代码示例:
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
m := make(map[string]int)
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)
mu.Lock()
m[key] = id
fmt.Printf("goroutine %d inserted key: %s, value: %d\n", id, key, id)
mu.Unlock()
}(i)
}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
mu.Lock()
delete(m, key)
fmt.Printf("goroutine %d deleted key: %s\n", id, key)
mu.Unlock()
}(i)
}
wg.Wait()
fmt.Println("Final map:", m)
}
- 使用sync.RWMutex
- 原理:读写锁(RWMutex)允许读操作并发执行,但写操作(包括删除操作)必须独占。这在有较多读操作和较少写操作的场景下,可以提高性能。
- 代码示例:
package main
import (
"fmt"
"sync"
)
func main() {
var rwmu sync.RWMutex
m := make(map[string]int)
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)
rwmu.RLock()
value, exists := m[key]
rwmu.RUnlock()
if exists {
fmt.Printf("goroutine %d read key: %s, value: %d\n", id, key, value)
} else {
fmt.Printf("goroutine %d key: %s not exists\n", id, key)
}
}(i)
}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
rwmu.Lock()
m[key] = id
fmt.Printf("goroutine %d inserted key: %s, value: %d\n", id, key, id)
rwmu.Unlock()
}(i)
}
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
rwmu.Lock()
delete(m, key)
fmt.Printf("goroutine %d deleted key: %s\n", id, key)
rwmu.Unlock()
}(i)
}
wg.Wait()
fmt.Println("Final map:", m)
}
- 使用sync.Map
- 原理:Go 1.9 引入的sync.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)
m.Store(key, id)
fmt.Printf("goroutine %d inserted key: %s, value: %d\n", id, key, id)
}(i)
}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
m.Delete(key)
fmt.Printf("goroutine %d deleted key: %s\n", id, key)
}(i)
}
wg.Wait()
m.Range(func(key, value interface{}) bool {
fmt.Printf("Final key: %v, value: %v\n", key, value)
return true
})
}