# Golang 并发编程进阶 ## 问题 1. sync.Mutex 和 sync.RWMutex 的区别? 2. sync.WaitGroup 和 sync.Once 的使用场景? 3. atomic 包的作用是什么? 4. 如何实现一个无锁队列? 5. 如何检测数据竞争(Data Race)? 6. Go 的内存模型(Happens-Before)是什么? --- ## 标准答案 ### 1. Mutex vs RWMutex #### **Mutex(互斥锁)** ```go var mu sync.Mutex func write() { mu.Lock() defer mu.Unlock() // 写操作 } ``` --- #### **RWMutex(读写锁)** ```go var mu sync.RWMutex func read() { mu.RLock() defer mu.RUnlock() // 读操作(多个 Goroutine 可以同时读) } func write() { mu.Lock() defer mu.Unlock() // 写操作(独占访问) } ``` --- **使用场景**: - **Mutex**:读写都少 - **RWMutex**:读多写少 --- ### 2. WaitGroup 和 Once #### **WaitGroup** **用途**:等待一组 Goroutine 完成 ```go var wg sync.WaitGroup func worker(id int) { defer wg.Done() fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { for i := 1; i <= 3; i++ { wg.Add(1) go worker(i) } wg.Wait() // 等待所有 worker 完成 fmt.Println("All workers done") } ``` --- #### **Once** **用途**:确保函数只执行一次 ```go var once sync.Once func initDB() { once.Do(func() { // 初始化数据库(只执行一次) fmt.Println("DB initialized") }) } func main() { initDB() initDB() // 不会再次执行 } ``` --- ### 3. atomic 包 #### **作用** **原子操作**:无锁并发编程 --- #### **示例** ```go import "sync/atomic" var counter int64 // ❌ 非原子操作(数据竞争) func increment() { counter++ // 不是原子操作 } // ✅ 原子操作 func incrementAtomic() { atomic.AddInt64(&counter, 1) } // 读取 value := atomic.LoadInt64(&counter) // 比较并交换(CAS) old := atomic.LoadInt64(&counter) atomic.CompareAndSwapInt64(&counter, old, new) ``` --- ### 4. 检测数据竞争 #### **使用 go test -race** ```bash # 检测数据竞争 go test -race ./... # 运行程序 go run -race main.go ``` --- #### **示例** ```go // ❌ 有数据竞争 func main() { var counter int go func() { counter++ // 数据竞争 }() counter++ // 数据竞争 } // ✅ 无数据竞争 func main() { var counter int64 go func() { atomic.AddInt64(&counter, 1) }() atomic.AddInt64(&counter, 1) } ``` --- ### 5. Happens-Before #### **定义** **Happens-Before**:保证一个操作对另一个操作可见 --- #### **规则** 1. **Goroutine 创建**:`go func()` happens-before `func()` 开始 2. **Channel**:发送 happens-before 接收 3. **Lock**:Unlock happens-before 下一次 Lock --- #### **示例** ```go var a, b int func main() { go func() { a = 1 // ① ch <- struct{}{} // ② }() b = 2 // ③ <-ch // ④ // 保证了: // ① happens-before ② // ② happens-before ④ // 因此:a = 1 一定会在 b = 2 之前执行 } ``` --- ### 6. 阿里 P7 加分项 **深度理解**: - 理解 Go 的内存模型(Happens-Before) - 理解 atomic 包的实现原理(CAS) - 理解 RWMutex 的实现(写锁饥饿问题) **实战经验**: - 有使用 -race 检测数据竞争的经验 - 有优化并发程序的经验(减少锁、使用 atomic) - 有实现无锁数据结构的经验 **并发编程**: - 理解如何避免数据竞争 - 理解如何使用原子操作 - 理解如何实现高性能并发程序