在 Golang中,同步锁主要用于多个 goroutine 并发访问共享资源时,防止数据竞争,保证线程安全。Go 提供了 sync 包来实现各种同步机制,其中最常用的是 互斥锁(Mutex) 和 读写锁(RWMutex)。
1. 互斥锁(sync.Mutex)
sync.Mutex 是最基本的同步锁,用于确保同一时间只有一个 goroutine 能访问临界区(共享资源)。
package main
import (
"fmt"
"sync"
)
var (
counter = 0
mutex sync.Mutex
wg sync.WaitGroup
)
func main() {
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// 加锁
mutex.Lock()
counter++
// 解锁
mutex.Unlock()
}()
}
wg.Wait()
fmt.Println("Final counter:", counter) // 应该输出 1000
}
特点:
2. 读写锁(sync.RWMutex)
当共享资源是“读多写少”的场景时,使用 RWMutex 可以提高并发性能。多个 goroutine 可以同时读(RLock)写操作独占(Lock),期间不允许读或写
package main
import (
"fmt"
"sync"
"time"
)
var (
data = make(map[string]int)
rwMux sync.RWMutex
wg sync.WaitGroup
)
func reader(id int) {
defer wg.Done()
rwMux.RLock() // 获取读锁
value := data["key"] // 读取共享数据
rwMux.RUnlock() // 释放读锁
fmt.Printf("Reader %d read value: %d
", id, value)
}
func writer() {
defer wg.Done()
rwMux.Lock() // 获取写锁(独占)
data["key"]++
time.Sleep(10 * time.Millisecond) // 模拟写操作耗时
rwMux.Unlock() // 释放写锁
}
func main() {
data["key"] = 0
// 启动 5 个写操作
for i := 0; i < 5; i++ {
wg.Add(1)
go writer()
}
// 启动 10 个读操作
for i := 0; i < 10; i++ {
wg.Add(1)
go reader(i)
}
wg.Wait()
fmt.Println("Final data:", data["key"])
}
⚠️ 注意事项
// ❌ 错误:可能死锁
mutex.Lock()
mutex.Lock() // 死锁!2. 使用 defer 确保解锁
mutex.Lock()
defer mutex.Unlock() // 即使 panic 也能释放锁
// 操作共享资源3. 锁的粒度要小
gomutex.Lock()
sharedData++
mutex.Unlock()// ❌ 不要把无关操作也包在锁里
// mutex.Lock()
// sharedData++
// time.Sleep(1) // 这样会降低并发性能
// mutex.Unlock()4. 不要复制包含锁的结构体
type Counter struct {
mu sync.Mutex
value int
}
c1 := Counter{}
c2 := c1 // ❌ 复制了 Mutex,可能导致未定义行为c2.mu.Lock()最佳实践
(Go 的哲学:“不要通过共享内存来通信,而应通过通信来共享内存”)