面试题答案
一键面试潜在问题分析
- 数据竞争:当多个goroutine同时读写相同的函数参数默认值时,就会发生数据竞争。例如,如果一个goroutine读取默认值,而另一个goroutine修改它,最终结果可能是不可预测的,因为读取操作可能在修改操作完成之前或之后发生,这取决于goroutine的调度。
- 不一致性:由于数据竞争,不同goroutine获取到的默认值可能不一致,导致程序逻辑出现错误。比如在一个计算任务中,不同goroutine基于不同的默认值进行计算,最终汇总结果就会出错。
解决方案
- 使用互斥锁(Mutex):通过互斥锁来保护对共享默认值的访问,确保同一时间只有一个goroutine可以访问和修改默认值。
- 使用原子操作:对于简单的数值类型,可以使用原子操作来避免数据竞争。原子操作在硬件层面保证了操作的原子性,不会被其他操作打断。
- 设计不可变数据结构:将默认值设计为不可变的数据结构,这样就无需担心并发修改的问题。如果需要修改,就创建一个新的实例。
数据结构设计
可以使用结构体来封装默认值和相关的同步机制。例如:
type SafeDefault struct {
value int
mu sync.Mutex
}
这里的SafeDefault
结构体包含一个整数值value
和一个互斥锁mu
,用于保护对value
的访问。
同步机制运用及代码示例
使用互斥锁
package main
import (
"fmt"
"sync"
)
type SafeDefault struct {
value int
mu sync.Mutex
}
func (s *SafeDefault) getValue() int {
s.mu.Lock()
defer s.mu.Unlock()
return s.value
}
func (s *SafeDefault) setValue(newValue int) {
s.mu.Lock()
defer s.mu.Unlock()
s.value = newValue
}
func main() {
var wg sync.WaitGroup
safeDefault := SafeDefault{value: 10}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
safeDefault.setValue(safeDefault.getValue() + id)
fmt.Printf("Goroutine %d updated value: %d\n", id, safeDefault.getValue())
}(i)
}
wg.Wait()
}
在这个示例中,SafeDefault
结构体的getValue
和setValue
方法通过互斥锁来保护对value
的读写操作,从而避免数据竞争。
使用原子操作(适用于数值类型)
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type AtomicDefault struct {
value int64
}
func (a *AtomicDefault) getValue() int64 {
return atomic.LoadInt64(&a.value)
}
func (a *AtomicDefault) setValue(newValue int64) {
atomic.StoreInt64(&a.value, newValue)
}
func main() {
var wg sync.WaitGroup
atomicDefault := AtomicDefault{value: 10}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
atomicDefault.setValue(atomicDefault.getValue() + int64(id))
fmt.Printf("Goroutine %d updated value: %d\n", id, atomicDefault.getValue())
}(i)
}
wg.Wait()
}
这里使用atomic
包的LoadInt64
和StoreInt64
函数来保证对int64
类型value
的原子读写操作,避免数据竞争。
使用不可变数据结构
package main
import (
"fmt"
"sync"
)
type ImmutableDefault struct {
value int
}
func (i ImmutableDefault) getValue() int {
return i.value
}
func (i ImmutableDefault) updateValue(newValue int) ImmutableDefault {
return ImmutableDefault{value: newValue}
}
func main() {
var wg sync.WaitGroup
immutableDefault := ImmutableDefault{value: 10}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
newDefault := immutableDefault.updateValue(immutableDefault.getValue() + id)
fmt.Printf("Goroutine %d new value: %d\n", id, newDefault.getValue())
}(i)
}
wg.Wait()
}
在这个示例中,ImmutableDefault
结构体的updateValue
方法返回一个新的实例,而不是修改原实例,从而避免了并发修改带来的问题。