面试题答案
一键面试挑战
- 数据竞争:在高并发环境下,多个 goroutine 可能同时尝试初始化切片,导致数据竞争问题,即不同 goroutine 对切片的初始化操作相互干扰,造成结果不可预测。
- 资源争用:如果初始化操作涉及到资源获取(如从数据库读取数据填充切片),多个 goroutine 同时进行初始化可能导致资源争用,降低系统性能。
解决方案
1. 使用sync.Once
sync.Once
类型提供了一种简单的机制来确保函数只执行一次,常用于延迟初始化。
package main
import (
"fmt"
"sync"
)
var (
data []int
once sync.Once
)
func getData() []int {
once.Do(func() {
// 模拟初始化操作,例如从数据库读取数据
data = make([]int, 10)
for i := range data {
data[i] = i
}
})
return data
}
在上述代码中,once.Do
确保 make([]int, 10)
以及后续的数据填充操作只执行一次,无论多少个 goroutine 调用 getData
函数,从而避免了数据竞争和重复初始化。
2. 使用互斥锁 sync.Mutex
通过互斥锁可以在初始化切片时进行加锁,防止多个 goroutine 同时进行初始化。
package main
import (
"fmt"
"sync"
)
var (
data []int
mu sync.Mutex
initialized bool
)
func getData() []int {
if!initialized {
mu.Lock()
defer mu.Unlock()
if!initialized {
// 模拟初始化操作
data = make([]int, 10)
for i := range data {
data[i] = i
}
initialized = true
}
}
return data
}
这里先进行快速检查 initialized
,如果未初始化则加锁,再次检查 initialized
以防止多个 goroutine 同时进入初始化部分,最后解锁。这种方式虽然比 sync.Once
复杂一些,但也能有效地实现线程安全的切片延迟初始化。