Changes: - Renamed all 10 Golang files from English to Chinese names - Created 00-项目概述/项目概述.md with comprehensive project overview - Created 08-算法与数据结构/算法与数据结构学习指南.md with detailed learning guide - Created 12-面试技巧/面试准备进度.md with progress tracking - Added .obsidian configuration for better markdown editing - Updated Claude.MD with Chinese filename rule Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
3.6 KiB
3.6 KiB
Golang 并发编程进阶
问题
- sync.Mutex 和 sync.RWMutex 的区别?
- sync.WaitGroup 和 sync.Once 的使用场景?
- atomic 包的作用是什么?
- 如何实现一个无锁队列?
- 如何检测数据竞争(Data Race)?
- Go 的内存模型(Happens-Before)是什么?
标准答案
1. Mutex vs RWMutex
Mutex(互斥锁)
var mu sync.Mutex
func write() {
mu.Lock()
defer mu.Unlock()
// 写操作
}
RWMutex(读写锁)
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 完成
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
用途:确保函数只执行一次
var once sync.Once
func initDB() {
once.Do(func() {
// 初始化数据库(只执行一次)
fmt.Println("DB initialized")
})
}
func main() {
initDB()
initDB() // 不会再次执行
}
3. atomic 包
作用
原子操作:无锁并发编程
示例
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
# 检测数据竞争
go test -race ./...
# 运行程序
go run -race main.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:保证一个操作对另一个操作可见
规则
- Goroutine 创建:
go func()happens-beforefunc()开始 - Channel:发送 happens-before 接收
- Lock:Unlock happens-before 下一次 Lock
示例
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)
- 有实现无锁数据结构的经验
并发编程:
- 理解如何避免数据竞争
- 理解如何使用原子操作
- 理解如何实现高性能并发程序