竞态检测

竞态条件

竞态条件的基本概念和示例。

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. 性能考虑
- 避免过度同步
- 使用适当的锁粒度
- 考虑无锁算法
- 使用性能分析工具