Appearance
并发安全
- 并发安全指的是多个 goroutine 同时访问共享资源时,不会出现数据竞争、逻辑错误或不一致的结果。
数据竞争的本质
- 数据竞争是指多个 goroutine 同时访问共享资源,且至少有一个 goroutine 对该资源进行写操作。
- 数据竞争会导致不可预测的结果,例如数据损坏、不一致的状态等。
解决方案
互斥锁 (Mutex)
- 排他锁,同一时间只允许一个 goroutine 访问资源(读 / 写互斥)。
go
var mu sync.Mutex
var count int
func main() {
defer mu.Unlock()
// 上锁
mu.Lock()
count++
}读写锁 (RWMutex)
- 读写锁,允许多个读操作并发,但写操作会阻塞所有读写。适合「读多写少」场景。
go
var rwMu sync.RWMutex
var data map[string]int
func readKey(key string) int {
rwMu.RLock() // 读锁
defer rwMu.RUnlock()
return data[key]
}
func writeKey(key string, val int) {
rwMu.Lock() // 写锁
defer rwMu.Unlock()
data[key] = val
}原子操作 (Atomic)
- 针对基本数据类型(如 int32、uint64 等)的简单操作,可使用原子函数避免锁的开销。
go
var counter int64
func add() {
atomic.AddInt64(&counter, 1) // 原子自增
}
func get() int64 {
return atomic.LoadInt64(&counter) // 原子读取
}通道
- 天然并发安全,无需显式锁。
go
var ch = make(chan int, 1) // 带缓冲的 channel 可减少阻塞
// 发送方
go func() {
ch <- 100 // 数据通过 channel 传递
}()
// 接收方
go func() {
val := <-ch // 安全获取数据
}()等待组
- 等待组,用于等待多个 goroutine 完成,防止主程序提前退出。
go
var wg sync.WaitGroup
func task() {
defer wg.Done() // 任务完成时减 1
// 业务逻辑
}
func main() {
wg.Add(5) // 启动 5 个任务
for i := 0; i < 5; i++ {
go task()
}
wg.Wait() // 等待所有任务完成
}sync.Once
- 确保某个操作只执行一次,常用于初始化操作。
- 示例:初始化数据库连接、加载配置文件、单例模式。
go
var once sync.Once
once.Do(func() {
// 初始化操作,只执行一次
})常见问题
- 死锁:多个 goroutine 等待对方释放锁,导致程序无法继续执行。
- 活锁:多个 goroutine 不断切换执行状态,导致程序无法继续执行。
- 饥饿:某个 goroutine 长期无法获取到锁,导致其他 goroutine 一直等待。
