Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

sync 包详解

概述

sync 包提供了基本的同步原语,如互斥锁。除了 OnceWaitGroup 类型外,大多数原语供底层库例程使用。更高级的同步最好通过通道和通信来实现。

核心功能

  • 互斥锁(Mutex、RWMutex)
  • 条件变量(Cond)
  • 一次性操作(Once)
  • 等待组(WaitGroup)
  • 对象池(Pool)
  • 并发安全的 Map(Map)
  • Once 辅助函数(OnceFunc、OnceValue、OnceValues)

重要说明

  • Go 版本:所有 Go 版本都支持
  • ⚠️ 禁止复制:包含 sync 类型的值不应被复制
  • ⚠️ 使用场景:大多数原语供底层库使用,高层同步建议使用 channel

包导入

import "sync"

接口

Locker

type Locker interface {
    Lock()
    Unlock()
}

功能: 表示可以锁定和解锁的对象。

实现类型

  • *Mutex
  • *RWMutex
  • RWMutex.RLocker() 返回的接口

示例

package main

import (
    "fmt"
    "sync"
)

func useLocker(locker sync.Locker) {
    locker.Lock()
    defer locker.Unlock()
    fmt.Println("锁已获取")
}

func main() {
    var mu sync.Mutex
    var rwmu sync.RWMutex
    
    useLocker(&mu)
    useLocker(rwmu.RLocker()) // 只读锁
}

类型详解(按 A-Z 分类)

C

Cond

type Cond struct {
    L Locker
}

功能: 实现条件变量,是等待或宣布事件发生的 goroutine 的集合点。

字段

  • L Locker - 关联的锁(通常是 *Mutex 或 *RWMutex)

重要说明

  • 每个 Cond 都有一个关联的 Locker L
  • 在改变条件和调用 Wait 时必须持有 L
  • Cond 首次使用后不能复制
  • 对于许多简单用例,使用 channel 比 Cond 更好

方法

  • Broadcast() - 唤醒所有等待的 goroutine
  • Signal() - 唤醒一个等待的 goroutine
  • Wait() - 等待直到被唤醒

示例

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var mu sync.Mutex
    cond := sync.NewCond(&mu)
    ready := false
    
    // 启动等待者
    go func() {
        mu.Lock()
        for !ready {
            cond.Wait()
        }
        fmt.Println("准备就绪!")
        mu.Unlock()
    }()
    
    time.Sleep(100 * time.Millisecond)
    
    // 通知
    mu.Lock()
    ready = true
    cond.Signal()
    mu.Unlock()
    
    time.Sleep(100 * time.Millisecond)
}

M

Map

type Map struct {
    // 包含过滤或未导出的字段
}

功能: 类似于 Go map[any]any,但对多个 goroutine 的并发使用是安全的,无需额外的锁或协调。

特点

  • 加载、存储和删除操作以分摊常数时间运行
  • 零值 Map 是空的且可直接使用
  • 首次使用后不能复制

优化场景

  1. 给定键的条目只写入一次但读取多次(如只增长的缓存)
  2. 多个 goroutine 为不相交的键集读取、写入和覆盖条目

方法

  • Clear() - 删除所有条目
  • CompareAndDelete(key, old any) - 比较并删除(Go 1.20+)
  • CompareAndSwap(key, old, new any) - 比较并交换(Go 1.20+)
  • Delete(key any) - 删除键
  • Load(key any) - 加载值
  • LoadAndDelete(key any) - 加载并删除(Go 1.15+)
  • LoadOrStore(key, value any) - 加载或存储
  • Range(f func(key, value any) bool) - 遍历
  • Store(key, value any) - 存储值
  • Swap(key, value any) - 交换值

示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    
    // Store
    m.Store("name", "Alice")
    m.Store("age", 25)
    
    // Load
    if v, ok := m.Load("name"); ok {
        fmt.Println(v) // Alice
    }
    
    // LoadOrStore
    v, loaded := m.LoadOrStore("age", 30)
    fmt.Println(v, loaded) // 25 true
    
    // Range
    m.Range(func(key, value any) bool {
        fmt.Printf("%s: %v\n", key, value)
        return true
    })
    
    // Delete
    m.Delete("age")
    
    // Swap (Go 1.20+)
    prev, loaded := m.Swap("name", "Bob")
    fmt.Println(prev, loaded) // Alice true
}

Mutex

type Mutex struct {
    // 包含过滤或未导出的字段
}

功能: 互斥锁,用于保护共享资源。

特点

  • 零值是未锁定的互斥锁
  • 首次使用后不能复制
  • 锁定的 Mutex 不与特定 goroutine 关联

方法

  • Lock() - 锁定
  • TryLock() - 尝试锁定
  • Unlock() - 解锁

示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    counter := 0
    
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.Lock()
            counter++
            mu.Unlock()
        }()
    }
    
    wg.Wait()
    fmt.Println(counter) // 1000
}

Mutex.TryLock

func (m *Mutex) TryLock() bool

功能: 尝试锁定 m 并报告是否成功。

注意

  • 正确使用 TryLock 的情况很少见
  • 使用 TryLock 通常是互斥锁使用中存在深层问题的标志

示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    
    if mu.TryLock() {
        fmt.Println("获取锁成功")
        mu.Unlock()
    }
    
    // 再次尝试
    mu.Lock()
    go func() {
        if !mu.TryLock() {
            fmt.Println("获取锁失败")
        }
    }()
}

O

Once

type Once struct {
    // 包含过滤或未导出的字段
}

功能: 将恰好执行一次操作的对象。

特点

  • 零值 Once 表示尚未执行
  • 首次使用后不能复制
  • 用于必须恰好运行一次的初始化

方法

  • Do(f func()) - 执行函数 f(仅一次)

示例

package main

import (
    "fmt"
    "sync"
)

var once sync.Once

func initOnce() {
    fmt.Println("初始化")
}

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            once.Do(initOnce)
        }()
    }
    
    wg.Wait()
    // 只输出一次:初始化
}

P

Pool

type Pool struct {
    New func() any
}

功能: 临时对象的集合,可以单独保存和检索。

目的

  • 缓存已分配但未使用的项以供以后重用
  • 减轻垃圾回收器的压力
  • 构建高效、线程安全的空闲列表

字段

  • New func() any - 当 Get 无法从池中获取对象时调用的函数

方法

  • Get() any - 从池中获取对象
  • Put(x any) - 将对象放回池中

注意

  • 池中存储的任何项都可能在任何时候被自动删除
  • 首次使用后不能复制

示例

package main

import (
    "bytes"
    "fmt"
    "sync"
)

var pool = sync.Pool{
    New: func() any {
        return new(bytes.Buffer)
    },
}

func main() {
    // 从池中获取
    buf := pool.Get().(*bytes.Buffer)
    buf.WriteString("Hello")
    fmt.Println(buf.String())
    
    // 放回池中
    buf.Reset()
    pool.Put(buf)
    
    // 再次获取(可能重用)
    buf2 := pool.Get().(*bytes.Buffer)
    fmt.Println("重用:", buf2)
}

R

RWMutex

type RWMutex struct {
    // 包含过滤或未导出的字段
}

功能: 读写互斥锁,可以被任意数量的读取者或单个写入者持有。

特点

  • 零值是未锁定的互斥锁
  • 首次使用后不能复制
  • 不支持递归读锁定
  • RLock 不能升级为 Lock,Lock 不能降级为 RLock

方法

  • Lock() - 锁定(写)
  • RLock() - 读锁定
  • RLocker() Locker - 返回只读 Locker 接口
  • RUnlock() - 解锁(读)
  • TryLock() - 尝试锁定(写)
  • TryRLock() - 尝试读锁定
  • Unlock() - 解锁(写)

示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    var rwmu sync.RWMutex
    data := make(map[string]int)
    
    // 写锁定
    rwmu.Lock()
    data["key"] = 42
    rwmu.Unlock()
    
    // 多个读锁定可以同时持有
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            rwmu.RLock()
            fmt.Println(data["key"])
            rwmu.RUnlock()
        }()
    }
    
    wg.Wait()
}

RWMutex.TryLock

func (rw *RWMutex) TryLock() bool

功能: 尝试锁定 rw 用于写入并报告是否成功。

注意

  • 正确使用 TryLock 的情况很少见
  • 通常是存在深层问题的标志

RWMutex.TryRLock

func (rw *RWMutex) TryRLock() bool

功能: 尝试锁定 rw 用于读取并报告是否成功。

W

WaitGroup

type WaitGroup struct {
    // 包含过滤或未导出的字段
}

功能: 计数信号量,通常用于等待一组 goroutine 完成。

特点

  • 零值 WaitGroup 是空的
  • 首次使用后不能复制
  • 计数器为负时会 panic

方法

  • Add(delta int) - 添加计数器
  • Done() - 减少计数器(等价于 Add(-1))
  • Go(f func()) - 启动新 goroutine 并添加到 WaitGroup(Go 1.25+)
  • Wait() - 等待直到计数器为零

使用模式

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    
    // 模式 1:使用 Add/Done
    wg.Add(2)
    go func() {
        defer wg.Done()
        fmt.Println("任务 1")
    }()
    go func() {
        defer wg.Done()
        fmt.Println("任务 2")
    }()
    wg.Wait()
    
    // 模式 2:使用 Go(Go 1.25+)
    // wg.Go(func() { fmt.Println("任务 3") })
    // wg.Go(func() { fmt.Println("任务 4") })
    // wg.Wait()
}

函数详解(按 A-Z 分类)

N

NewCond

func NewCond(l Locker) *Cond

功能: 返回一个带有 Locker l 的新 Cond。

参数

  • l Locker - 关联的锁

返回值

  • *Cond - 新的条件变量

示例

package main

import (
    "sync"
)

func main() {
    var mu sync.Mutex
    cond := sync.NewCond(&mu)
    
    // 使用 cond...
    _ = cond
}

O

OnceFunc

func OnceFunc(f func()) func()

功能: 返回一个只调用 f 一次的函数。返回的函数可以并发调用。

参数

  • f func() - 要执行的函数

返回值

  • func() - 包装后的函数

注意

  • 如果 f panic,返回的函数每次调用都会 panic

示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    f := sync.OnceFunc(func() {
        fmt.Println("只执行一次")
    })
    
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            f()
        }()
    }
    
    wg.Wait()
    // 只输出一次:只执行一次
}

OnceValue

func OnceValue[T any](f func() T) func() T

功能: 返回一个只调用 f 一次并返回 f 返回的值的函数。返回的函数可以并发调用。

参数

  • f func() T - 要执行的函数

返回值

  • func() T - 包装后的函数

注意

  • Go 1.21+
  • 如果 f panic,返回的函数每次调用都会 panic

示例

package main

import (
    "fmt"
    "sync"
)

func expensiveComputation() int {
    fmt.Println("执行昂贵计算")
    sum := 0
    for i := 1; i <= 100; i++ {
        sum += i
    }
    return sum
}

func main() {
    getValue := sync.OnceValue(expensiveComputation)
    
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            fmt.Println(getValue())
        }()
    }
    
    wg.Wait()
    // 只输出一次:执行昂贵计算
    // 然后输出 5 次:5050
}

OnceValues

func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)

功能: 返回一个只调用 f 一次并返回 f 返回的值的函数。返回的函数可以并发调用。

参数

  • f func() (T1, T2) - 要执行的函数

返回值

  • func() (T1, T2) - 包装后的函数

注意

  • Go 1.21+
  • 如果 f panic,返回的函数每次调用都会 panic

示例

package main

import (
    "fmt"
    "sync"
)

func readFile() (string, error) {
    fmt.Println("读取文件")
    return "file content", nil
}

func main() {
    readFileOnce := sync.OnceValues(readFile)
    
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            content, err := readFileOnce()
            fmt.Println(content, err)
        }()
    }
    
    wg.Wait()
    // 只输出一次:读取文件
    // 然后输出 3 次:file content <nil>
}

典型示例

示例 1:使用 Mutex 保护共享资源

package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.value++
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

func main() {
    counter := &Counter{}
    
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.Increment()
        }()
    }
    
    wg.Wait()
    fmt.Println(counter.Value()) // 1000
}

运行结果

1000

示例 2:使用 RWMutex 优化读多写少

package main

import (
    "fmt"
    "sync"
)

type Cache struct {
    mu    sync.RWMutex
    items map[string]string
}

func NewCache() *Cache {
    return &Cache{items: make(map[string]string)}
}

func (c *Cache) Get(key string) (string, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()
    val, ok := c.items[key]
    return val, ok
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.items[key] = value
}

func main() {
    cache := NewCache()
    
    var wg sync.WaitGroup
    
    // 多个读者
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            if val, ok := cache.Get("key"); ok {
                fmt.Printf("Reader %d: %s\n", id, val)
            }
        }(i)
    }
    
    // 一个写者
    wg.Add(1)
    go func() {
        defer wg.Done()
        cache.Set("key", "value")
        fmt.Println("Writer: set value")
    }()
    
    wg.Wait()
}

运行结果

Writer: set value
Reader 0: value
Reader 1: value
Reader 2: value
Reader 3: value
Reader 4: value

示例 3:使用 WaitGroup 等待多个 goroutine

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d 开始\n", id)
    time.Sleep(100 * time.Millisecond)
    fmt.Printf("Worker %d 完成\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    wg.Wait()
    fmt.Println("所有 worker 完成")
}

运行结果

Worker 1 开始
Worker 2 开始
Worker 3 开始
Worker 4 开始
Worker 5 开始
Worker 1 完成
Worker 2 完成
Worker 3 完成
Worker 4 完成
Worker 5 完成
所有 worker 完成

示例 4:使用 Once 进行单次初始化

package main

import (
    "fmt"
    "sync"
)

var (
    config     map[string]string
    configOnce sync.Once
)

func loadConfig() {
    fmt.Println("加载配置...")
    config = map[string]string{
        "host": "localhost",
        "port": "8080",
    }
}

func getConfig() map[string]string {
    configOnce.Do(loadConfig)
    return config
}

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            cfg := getConfig()
            fmt.Printf("Goroutine %d: %v\n", id, cfg)
        }(i)
    }
    
    wg.Wait()
}

运行结果

加载配置...
Goroutine 0: map[host:localhost port:8080]
Goroutine 1: map[host:localhost port:8080]
Goroutine 2: map[host:localhost port:8080]
Goroutine 3: map[host:localhost port:8080]
Goroutine 4: map[host:localhost port:8080]

示例 5:使用 Cond 实现条件等待

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var mu sync.Mutex
    cond := sync.NewCond(&mu)
    done := false
    
    // 启动 3 个等待者
    for i := 0; i < 3; i++ {
        go func(id int) {
            mu.Lock()
            for !done {
                cond.Wait()
            }
            fmt.Printf("Goroutine %d 被唤醒\n", id)
            mu.Unlock()
        }(i)
    }
    
    time.Sleep(100 * time.Millisecond)
    
    // 通知所有等待者
    mu.Lock()
    done = true
    cond.Broadcast()
    mu.Unlock()
    
    time.Sleep(100 * time.Millisecond)
}

运行结果

Goroutine 0 被唤醒
Goroutine 1 被唤醒
Goroutine 2 被唤醒

示例 6:使用 Pool 减少 GC 压力

package main

import (
    "bytes"
    "fmt"
    "sync"
)

var bufferPool = sync.Pool{
    New: func() any {
        return new(bytes.Buffer)
    },
}

func process(data string) {
    buf := bufferPool.Get().(*bytes.Buffer)
    buf.Reset()
    
    buf.WriteString("Processing: ")
    buf.WriteString(data)
    
    fmt.Println(buf.String())
    
    bufferPool.Put(buf)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            process(fmt.Sprintf("data-%d", id))
        }(i)
    }
    
    wg.Wait()
}

运行结果

Processing: data-0
Processing: data-1
Processing: data-2
Processing: data-3
Processing: data-4

示例 7:使用 Map 进行并发安全的键值存储

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    
    var wg sync.WaitGroup
    
    // 并发写入
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            m.Store(fmt.Sprintf("key-%d", id), id*10)
        }(i)
    }
    
    wg.Wait()
    
    // 并发读取
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            if val, ok := m.Load(fmt.Sprintf("key-%d", id)); ok {
                fmt.Printf("key-%d: %v\n", id, val)
            }
        }(i)
    }
    
    wg.Wait()
    
    // 遍历
    m.Range(func(key, value any) bool {
        fmt.Printf("%v: %v\n", key, value)
        return true
    })
}

运行结果

key-0: 0
key-1: 10
key-2: 20
key-3: 30
key-4: 40
key-0: 0
key-1: 10
key-2: 20
key-3: 30
key-4: 40

示例 8:使用 OnceValue 缓存昂贵计算

package main

import (
    "fmt"
    "sync"
    "time"
)

var compute = sync.OnceValue(func() int {
    fmt.Println("执行昂贵计算...")
    time.Sleep(100 * time.Millisecond)
    return 42
})

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            result := compute()
            fmt.Printf("Goroutine %d: %d\n", id, result)
        }(i)
    }
    
    wg.Wait()
}

运行结果

执行昂贵计算...
Goroutine 0: 42
Goroutine 1: 42
Goroutine 2: 42
Goroutine 3: 42
Goroutine 4: 42

最佳实践

1. 选择合适的同步原语

// ✅ 推荐:简单共享状态使用 Mutex
var mu sync.Mutex
var counter int

// ✅ 推荐:读多写少使用 RWMutex
var rwmu sync.RWMutex
var cache map[string]string

// ✅ 推荐:等待多个 goroutine 使用 WaitGroup
var wg sync.WaitGroup

// ✅ 推荐:单次初始化使用 Once
var once sync.Once

// ✅ 推荐:对象复用使用 Pool
var pool = sync.Pool{New: func() any { return &Buffer{} }}

2. 使用 defer 释放锁

// ✅ 推荐
mu.Lock()
defer mu.Unlock()
// 操作共享资源

// ❌ 不推荐:容易忘记解锁
mu.Lock()
// 操作共享资源
mu.Unlock() // 如果前面 panic,这里不会执行

3. 避免复制 sync 类型

// ✅ 推荐:使用指针
func process(m *sync.Mutex) {
    m.Lock()
    defer m.Unlock()
}

// ❌ 不推荐:复制 sync 类型
func process(m sync.Mutex) { // 会复制
    m.Lock()
    defer m.Unlock()
}

4. WaitGroup 正确使用 Add

// ✅ 推荐:在 goroutine 之前 Add
wg.Add(1)
go func() {
    defer wg.Done()
    // 工作
}()

// ❌ 不推荐:在 goroutine 内部 Add(可能 race)
go func() {
    wg.Add(1) // 可能 Wait 已经调用
    defer wg.Done()
    // 工作
}()

5. Cond 使用循环检查条件

// ✅ 推荐
mu.Lock()
for !condition() {
    cond.Wait()
}
// 使用条件
mu.Unlock()

// ❌ 不推荐:可能被虚假唤醒
mu.Lock()
if !condition() {
    cond.Wait()
}
mu.Unlock()

与其他包配合

与 context 包配合

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    defer cancel()
    
    var wg sync.WaitGroup
    
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            select {
            case <-time.After(100 * time.Millisecond):
                fmt.Printf("Worker %d 完成\n", id)
            case <-ctx.Done():
                fmt.Printf("Worker %d 被取消\n", id)
            }
        }(i)
    }
    
    wg.Wait()
}

与 channel 配合

package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        results <- job * 2
    }
}

func main() {
    jobs := make(chan int, 10)
    results := make(chan int, 10)
    
    var wg sync.WaitGroup
    
    // 启动 3 个 worker
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go worker(i, jobs, results, &wg)
    }
    
    // 发送任务
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)
    
    // 等待 worker 完成
    go func() {
        wg.Wait()
        close(results)
    }()
    
    // 收集结果
    for result := range results {
        fmt.Println(result)
    }
}

注意事项

限制

  1. 禁止复制

    • 所有 sync 类型首次使用后都不能复制
    • 包含 sync 类型的结构体也应避免复制
  2. 死锁风险

    • Mutex 重复锁定会死锁
    • RWMutex 不支持递归读锁定
  3. 性能考虑

    • Mutex 在高竞争场景性能下降
    • Pool 不保证对象一定被重用
  4. Cond 使用限制

    • 必须在持有锁的情况下调用 Wait
    • 必须使用循环检查条件

使用建议

  1. 优先使用 channel

    // ✅ 推荐:使用 channel 进行高层同步
    done := make(chan struct{})
    <-done
    
    // ⚠️ 仅在底层库使用 sync 原语
    
  2. 避免过度使用 TryLock

    // ⚠️ TryLock 通常是设计问题的标志
    if mu.TryLock() {
        // ...
    }
    
  3. Pool 的 New 函数

    // ✅ 推荐:提供 New 函数
    var pool = sync.Pool{
        New: func() any { return &Buffer{} },
    }
    
    // ⚠️ 不推荐:没有 New 函数可能返回 nil
    var pool = sync.Pool{}
    
  4. WaitGroup 重用

    // ✅ 推荐:Wait 返回后才能重用
    wg.Wait()
    // 现在可以重用 wg
    wg.Add(1)
    

快速参考

类型速查表

类型功能主要方法
Cond条件变量Broadcast, Signal, Wait
Locker锁接口Lock, Unlock
Map并发 MapLoad, Store, Delete, Range
Mutex互斥锁Lock, Unlock, TryLock
Once单次执行Do
Pool对象池Get, Put
RWMutex读写锁Lock, RLock, Unlock, RUnlock
WaitGroup等待组Add, Done, Wait, Go

函数速查表

函数功能Go 版本
NewCond创建 Cond所有
OnceFunc返回只执行一次的函数1.21+
OnceValue返回只执行一次并返回值的函数1.21+
OnceValues返回只执行一次并返回多值的函数1.21+

常见模式

// 1. Mutex 保护共享资源
mu.Lock()
defer mu.Unlock()
// 操作共享资源

// 2. RWMutex 读多写少
rwmu.RLock()
defer rwmu.RUnlock()
// 读取

rwmu.Lock()
defer rwmu.Unlock()
// 写入

// 3. WaitGroup 等待多个 goroutine
wg.Add(1)
go func() {
    defer wg.Done()
    // 工作
}()
wg.Wait()

// 4. Once 单次初始化
once.Do(func() {
    // 初始化代码
})

// 5. Pool 对象复用
obj := pool.Get()
// 使用 obj
pool.Put(obj)

// 6. Cond 条件等待
mu.Lock()
for !condition {
    cond.Wait()
}
// 条件满足
mu.Unlock()

// 7. Map 并发安全存储
m.Store(key, value)
v, ok := m.Load(key)
m.Delete(key)
m.Range(func(k, v any) bool {
    // 处理
    return true
})

选择指南

场景推荐原语
保护共享资源Mutex
读多写少RWMutex
等待多个 goroutineWaitGroup
单次初始化Once
对象复用Pool
条件等待Cond
并发 Mapsync.Map
高层同步channel

总结

sync 包是 Go 标准库中用于并发同步的核心包。

核心优势

  • ✅ 提供基础同步原语
  • ✅ 性能优秀
  • ✅ 线程安全
  • ✅ 支持多种同步模式

重要限制

  • ⚠️ 禁止复制 sync 类型
  • ⚠️ 大多数原语供底层库使用
  • ⚠️ 高层同步建议使用 channel

主要用途

  • 互斥锁(Mutex、RWMutex)
  • 条件变量(Cond)
  • 单次执行(Once)
  • 等待组(WaitGroup)
  • 对象池(Pool)
  • 并发 Map(Map)

使用建议

  1. 优先使用 channel 进行高层同步
  2. 使用 defer 释放锁
  3. 避免复制 sync 类型
  4. WaitGroup 在 goroutine 之前 Add
  5. Cond 使用循环检查条件

性能提示

  • RWMutex 在读多写少场景性能更好
  • Pool 可以减少 GC 压力
  • sync.Map 在特定场景比普通 map+Mutex 性能更好