引言
在 Go 语言中,sync 包提供了一系列用于同步 goroutine 的工具。这些工具可以帮助开发者避免竞态条件、管理并发任务的完成状态、保护共享资源的一致性等。
WaitGroup
- 概述:
- WaitGroup 用于同步等待一组 goroutine 的完成。
- 示例:
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
// 执行任务...
}()
wg.Wait()
- 基本用法:
- 调用 Add 方法增加等待计数。
- 调用 Done 方法减少等待计数。
- 调用 Wait 方法等待所有 goroutine 完成。
Mutex(互斥锁)
- 概述:
- Mutex 提供了互斥锁的功能,用于保护对共享资源的独占访问。
- 示例:
var mu sync.Mutex mu.Lock() // 访问共享资源... mu.Unlock()
- 基本用法:
- 调用 Lock 方法获取锁。
- 调用 Unlock 方法释放锁。
- 示例:
var mu sync.Mutex
var count int
go func() {
mu.Lock()
count++
mu.Unlock()
}()
Once
- 概述:
- Once 确保某个函数只被执行一次。
- 示例:
var once sync.Once
once.Do(func() {
// 初始化代码...
})
- 基本用法:
- 调用 Do 方法来执行函数。
- 示例:
var once sync.Once
var initialized bool
func initConfig() {
// 配置初始化...
initialized = true
}
go func() {
once.Do(initConfig)
}()
RWMutex(读写锁)
- 概述:
- RWMutex 提供读写锁的功能,允许多个 goroutine 同时读取共享资源,但在写入时需要独占访问。
- 示例:
var rwmu sync.RWMutex
rwmu.RLock()
// 读取共享资源...
rwmu.RUnlock()
- 基本用法:
- 调用 RLock 和 RUnlock 方法获取和释放读锁。
- 调用 Lock 和 Unlock 方法获取和释放写锁。
- 示例:
var rwmu sync.RWMutex
var data map[string]int
go func() {
rwmu.Lock()
data["key"] = 42
rwmu.Unlock()
}()
Cond
- 概述:
- Cond 提供条件变量的功能,用于在 goroutine 之间同步等待特定条件满足。
- 示例:
var cond sync.Cond
cond.L.Lock()
cond.Wait()
cond.L.Unlock()
- 基本用法:
- 调用 New 方法创建一个新的条件变量。
- 调用 Wait 方法让 goroutine 等待。
- 调用 Signal 或 Broadcast 方法唤醒等待的 goroutine。
- 示例:
var cond sync.Cond
var mu sync.Mutex
var ready bool
cond = sync.NewCond(&mu)
go func() {
mu.Lock()
cond.Wait()
eady = true
mu.Unlock()
}()
go func() {
mu.Lock()
cond.Signal()
mu.Unlock()
}()
编码实践
// https://go.dev/play/p/D4ForBr2txT
package main
import (
"fmt"
"sync"
"time"
)
func main() {
// 使用 WaitGroup 等待 goroutine 完成
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Hello from goroutine!")
}()
wg.Wait()
// 使用 Mutex 保护共享资源
var mu sync.Mutex
var count int
go func() {
mu.Lock()
count++
mu.Unlock()
}()
mu.Lock()
fmt.Println("Count:", count)
mu.Unlock()
// 使用 Once 确保初始化只执行一次
var once sync.Once
var initialized bool
once.Do(func() {
initialized = true
fmt.Println("Initialized")
})
once.Do(func() {
// 不会被执行
})
_ = initialized
// 使用 RWMutex 允许多个 goroutine 同时读取
var rwmu sync.RWMutex
var data map[string]int
data = make(map[string]int)
data["key"] = 42
go func() {
rwmu.RLock()
fmt.Println("Data:", data["key"])
rwmu.RUnlock()
}()
go func() {
rwmu.Lock()
data["key"] = 99
rwmu.Unlock()
}()
// 使用 Cond 等待特定条件
lock := sync.Mutex{}
var ready bool
cond := sync.NewCond(&lock)
go func() {
cond.L.Lock()
cond.Wait()
ready = true
cond.L.Unlock()
}()
go func() {
time.Sleep(2 * time.Second)
cond.L.Lock()
cond.Signal()
cond.L.Unlock()
}()
time.Sleep(3 * time.Second)
fmt.Println("Ready:", ready)
}