Files
interview/questions/13-Golang语言/并发编程进阶.md
yasinshaw 7f3ab362b3 feat: rename Golang files to Chinese and supplement root files
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>
2026-03-01 00:33:32 +08:00

3.6 KiB
Raw Blame History

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互斥锁

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:保证一个操作对另一个操作可见


规则

  1. Goroutine 创建go func() happens-before func() 开始
  2. Channel:发送 happens-before 接收
  3. LockUnlock 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
  • 有实现无锁数据结构的经验

并发编程

  • 理解如何避免数据竞争
  • 理解如何使用原子操作
  • 理解如何实现高性能并发程序