竞态检测
竞态条件
竞态条件的基本概念和示例。
package main
import (
"fmt"
"sync"
)
// 竞态条件示例
func raceExample() {
var counter int
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter++ // 竞态条件
}()
}
wg.Wait()
fmt.Println("Counter:", counter) // 结果不确定
}
// 使用互斥锁解决竞态条件
func safeExample() {
var counter int
var wg sync.WaitGroup
var mu sync.Mutex
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
counter++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("Counter:", counter) // 结果确定
}
竞态检测器
使用Go的竞态检测器。
# 运行竞态检测
go run -race main.go
# 测试竞态检测
go test -race ./...
# 构建带竞态检测的二进制文件
go build -race
# 竞态检测输出示例
WARNING: DATA RACE
Write at 0x00c00001a0a8 by goroutine 7:
main.raceExample.func1()
/path/to/main.go:15 +0x44
main.raceExample()
/path/to/main.go:13 +0x7c
Previous read at 0x00c00001a0a8 by goroutine 6:
main.raceExample.func1()
/path/to/main.go:15 +0x44
main.raceExample()
/path/to/main.go:13 +0x7c
Goroutine 7 (running) created at:
main.raceExample()
/path/to/main.go:13 +0x7c
Goroutine 6 (finished) created at:
main.raceExample()
/path/to/main.go:13 +0x7c
常见竞态模式
常见的竞态条件模式。
// 1. 共享变量访问
var shared int
go func() { shared++ }()
go func() { shared++ }()
// 2. 切片并发访问
slice := make([]int, 10)
go func() { slice[0] = 1 }()
go func() { slice[0] = 2 }()
// 3. map并发访问
m := make(map[string]int)
go func() { m["key"] = 1 }()
go func() { m["key"] = 2 }()
// 4. 接口并发访问
var i interface{}
go func() { i = 1 }()
go func() { i = "hello" }()
// 5. 结构体字段并发访问
type Counter struct {
value int
}
c := &Counter{}
go func() { c.value++ }()
go func() { c.value++ }()
解决方案
解决竞态条件的方法。
// 1. 使用互斥锁
var mu sync.Mutex
mu.Lock()
// 临界区
mu.Unlock()
// 2. 使用读写锁
var rwmu sync.RWMutex
rwmu.RLock() // 读锁
// 读操作
rwmu.RUnlock()
rwmu.Lock() // 写锁
// 写操作
rwmu.Unlock()
// 3. 使用原子操作
var value atomic.Value
value.Store(1)
value.Load()
// 4. 使用channel
ch := make(chan int)
go func() { ch <- 1 }()
go func() { ch <- 2 }()
// 5. 使用sync.Once
var once sync.Once
once.Do(func() {
// 只执行一次
})
// 6. 使用sync.WaitGroup
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
// 工作
}()
wg.Wait()
最佳实践
避免竞态条件的最佳实践。
# 1. 设计原则
- 最小化共享状态
- 使用不可变数据
- 使用消息传递
- 避免全局变量
# 2. 代码组织
- 将并发逻辑封装
- 使用接口隔离
- 保持函数纯粹
- 避免副作用
# 3. 测试策略
- 编写并发测试
- 使用竞态检测
- 压力测试
- 边界测试
# 4. 调试技巧
- 使用日志
- 使用调试器
- 使用pprof
- 使用trace
# 5. 性能考虑
- 避免过度同步
- 使用适当的锁粒度
- 考虑无锁算法
- 使用性能分析工具