面试题答案
一键面试竞态条件问题分析
在Go的并发编程中,当多个goroutine同时读写布尔类型变量时,可能会出现竞态条件。因为CPU对变量的读写操作并非原子的,在多个goroutine并发访问和修改布尔变量时,可能会导致数据不一致的问题。例如,一个goroutine读取变量的值,另一个goroutine同时修改该值,就可能导致读取到的值不是预期的。
解决方案一:互斥锁(sync.Mutex)
- 优点:
- 简单直观,Go语言标准库提供的
sync.Mutex
使用方便。 - 能够有效保护共享资源,避免竞态条件。
- 简单直观,Go语言标准库提供的
- 缺点:
- 性能开销较大,每次读写操作都需要获取和释放锁,可能会导致性能瓶颈,尤其是在高并发场景下。
- 容易引发死锁,例如在嵌套锁的情况下,如果获取锁的顺序不当。
- 代码示例:
package main
import (
"fmt"
"sync"
)
var (
flag bool
mutex sync.Mutex
)
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
mutex.Lock()
flag = true
mutex.Unlock()
}()
go func() {
defer wg.Done()
mutex.Lock()
result := flag
mutex.Unlock()
fmt.Println("Result:", result)
}()
wg.Wait()
}
解决方案二:原子操作(sync/atomic)
- 优点:
- 性能高效,原子操作在硬件层面保证了操作的原子性,不需要像互斥锁那样进行复杂的加锁和解锁操作。
- 不会引发死锁问题,因为不存在锁的获取和释放顺序问题。
- 缺点:
- 功能相对有限,只能进行一些基本的原子操作,如比较并交换(CAS)等,对于复杂的布尔代数运算需要进行额外的处理。
- 代码相对复杂,需要对原子操作有深入理解才能正确使用。
- 代码示例:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var flag int32
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
atomic.StoreInt32(&flag, 1)
}()
go func() {
defer wg.Done()
result := atomic.LoadInt32(&flag) != 0
fmt.Println("Result:", result)
}()
wg.Wait()
}