Added comprehensive Golang interview preparation materials:
- 基础语法(值类型、切片、map、defer、接口、struct、new/make)
- Goroutine 和并发模型(与线程对比、调度模型、内存模型)
- 错误处理和测试(error、panic/recover、单元测试、Benchmark)
- 并发编程进阶(Mutex、RWMutex、WaitGroup、atomic、数据竞争)
- HTTP 和 Web 开发(Client、Server、中间件模式)
- 内存模型和垃圾回收(内存分配、逃逸分析、GC)
- 性能优化(pprof、内存优化、CPU优化、并发优化)
- 反射和 unsafe(反射性能、unsafe 使用场景)
- 接口和类型系统(类型断言、interface{}、类型嵌入、泛型)
- 数据库操作(database/sql、GORM、事务、SQL 注入防护)
- 项目结构和工程化(标准项目结构、Go Module、CI/CD)
Each file includes:
- Detailed questions and comprehensive answers
- Code examples and best practices
- Alibaba P7 level requirements
Total: 60 interview questions (50 backend + 10 Golang)
Generated with [Claude Code](https://claude.com/claude-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)
- 有实现无锁数据结构的经验
并发编程:
- 理解如何避免数据竞争
- 理解如何使用原子操作
- 理解如何实现高性能并发程序