Go 中的锁
Go 语言提供了多种锁机制,用于在并发编程中保护共享资源。常见的锁包括 互斥锁、读写锁 和 sync.Map 的安全锁。
1. 互斥锁(Mutex)
原理
互斥锁(sync.Mutex
)是一种最简单的锁机制,用于保护共享资源的独占访问。当一个 Goroutine 获取了互斥锁后,其他 Goroutine 必须等待锁释放才能访问共享资源。
特点
- 独占访问:同一时间只有一个 Goroutine 可以持有锁。
- 阻塞等待:未获取锁的 Goroutine 会阻塞,直到锁被释放。
- 简单易用:适用于简单的临界区保护。
使用场景
- 适用于对共享资源的独占访问场景。
- 适用于临界区较小的场景。
2. 读写锁(RWMutex)
原理
读写锁(sync.RWMutex
)是一种更高效的锁机制,允许多个 Goroutine 同时读取共享资源,但只允许一个 Goroutine 写入共享资源。
特点
- 读共享:多个 Goroutine 可以同时获取读锁。
- 写独占:只有一个 Goroutine 可以获取写锁,且写锁与读锁互斥。
- 高效并发:适用于读多写少的场景。
使用场景
- 适用于读操作远多于写操作的场景。
- 适用于需要高并发读取共享资源的场景。
3. sync.Map 的安全锁
原理
sync.Map
是 Go 语言提供的一种并发安全的映射类型,内部通过锁机制和分段存储实现高效的并发访问。
特点
- 并发安全:无需额外加锁即可安全地并发访问。
- 高效读写:通过分段锁和原子操作优化性能。
- 动态扩展:支持动态扩展存储空间。
使用场景
- 适用于需要高并发读写的映射场景。
- 适用于键值对数量动态变化的场景。
以下是 Go 语言中三种锁的工作原理流程图:
三种锁使用实例
互斥锁
package mainimport ("fmt""sync""time"
)var (counter intmutex sync.Mutex
)func increment() {mutex.Lock() // 加锁counter++mutex.Unlock() // 解锁
}func main() {var wg sync.WaitGroupfor i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()increment()}()}wg.Wait()fmt.Println("Final Counter Value:", counter)
}
Final Counter Value: 1000
读写锁(RWMutex)
package mainimport ("fmt""sync""time"
)var (data map[string]stringrwMutex sync.RWMutex
)func readData(key string) string {rwMutex.RLock() // 加读锁defer rwMutex.RUnlock() // 解读锁return data[key]
}func writeData(key, value string) {rwMutex.Lock() // 加写锁defer rwMutex.Unlock() // 解写锁data[key] = value
}func main() {data = make(map[string]string)var wg sync.WaitGroup// 写操作wg.Add(1)go func() {defer wg.Done()writeData("name", "Alice")}()// 读操作wg.Add(1)go func() {defer wg.Done()fmt.Println("Name:", readData("name"))}()wg.Wait()
}
Name: Alice
sync.Map 的安全锁
package mainimport ("fmt""sync"
)func main() {var sm sync.Map// 存储数据sm.Store("name", "Alice")sm.Store("age", 30)// 读取数据if value, ok := sm.Load("name"); ok {fmt.Println("Name:", value)}// 遍历数据sm.Range(func(key, value interface{}) bool {fmt.Printf("Key: %v, Value: %v\n", key, value)return true})
}
Name: Alice
Key: name, Value: Alice
Key: age, Value: 30