跳到主要内容

Go 语言中的锁类型与应用场景详解

· 阅读需 3 分钟
素明诚
Full stack development

常用的锁的类型​

锁类型描述用途
Mutex互斥锁,一次只允许一个 goroutine 访问某个资源。用于保护共享资源,避免并发写入时出现数据竞争。
RWMutex读写锁,允许多个读操作同时进行,但写操作是互斥的。适用于读多写少的场景,可以提高在频繁读取时的性能。
Once一次性锁,确保某个操作在全局范围内只被执行一次。主要用于资源的初始化,如单例模式的实现中保证只初始化一次。
Cond条件变量,用于等待或宣布事件的发生。当 goroutine 需要等待某个条件满足时使用,常与 Mutex 或 RWMutex 配合使用。

RWMutex vs. Mutex​

Mutex(互斥锁)​

  • 功能:提供基本的锁定功能,确保同一时刻只有一个 goroutine 可以访问资源。
  • 适用场景:适用于对数据进行频繁写操作的场景。

RWMutex(读写锁)​

  • 优势:允许多个读取操作同时进行,而写操作则需独占资源。
  • 性能优化:在读多写少的应用场景中,RWMutex 比 Mutex 更高效,因为它减少了读操作的等待时间。

代码示例​

Mutex(互斥锁)​

package main
​
import (
"fmt"
"sync"
)
​
var mutex sync.Mutex
var balance int
​
func Deposit(amount int) {
defer mutex.Unlock()
mutex.Lock()
balance += amount
}
​
func Balance() int {
defer mutex.Unlock()
mutex.Lock()
return balance
}
​
func main() {
Deposit(100)
fmt.Println("Current Balance:", Balance())
}
​

RWMutex(读写锁)​

package main
​
import (
"fmt"
"sync"
)
​
var rwMutex sync.RWMutex
var data2 int
var wg sync.WaitGroup
​
func ReadData(id int) {
defer rwMutex.RUnlock()
rwMutex.RLock()
fmt.Printf("Goroutine %d read data: %d\n", id, data2)
​
wg.Done()
}
​
func WriteData(id, d int) {
defer rwMutex.Unlock()
rwMutex.Lock()
data2 = d
fmt.Printf("Goroutine %d write data: %d\n", id, d)
wg.Done()
}
​
func main() {
// 启动多个读写goroutines
numGoroutines := 10
wg.Add(numGoroutines * 2) // 因为有numGoroutines个读goroutine和numGoroutines个写goroutine
​
for i := 0; i < numGoroutines; i++ {
go WriteData(i, i*10) // 每个goroutine写入不同的数据
go ReadData(i) // 同时进行读操作
}
​
wg.Wait() // 等待所有goroutine完成
fmt.Println("Final Data:", data2)
}
​

Once(一次性锁)​

package main
​
import (
"fmt"
"sync"
)
​
var once sync.Once
var value int
​
func Setup() {
value = 42
fmt.Println("Value set")
}
​
func DoSetup() {
once.Do(Setup)
}
​
func main() {
go DoSetup()
go DoSetup()
go DoSetup()
​
// WaitGroup 等待 goroutines 完成
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
DoSetup()
}()
go func() {
defer wg.Done()
DoSetup()
}()
go func() {
defer wg.Done()
DoSetup()
}()
wg.Wait()
fmt.Println("value:", value)
}

Cond(条件变量)​

package main
​
import (
"sync"
"time"
)
​
var cond = sync.NewCond(&sync.Mutex{})
var ready bool
var wg4 sync.WaitGroup
​
// process 处理特定的进程,等待条件变量通知
func process(i int) {
defer wg4.Done()
defer cond.L.Unlock()
cond.L.Lock()
for !ready {
cond.Wait()
}
println("进程", i, "已准备就绪")
}
​
func main() {
for i := 0; i < 5; i++ {
wg4.Add(1)
go process(i)
}
println("所有协程已创建")
​
time.Sleep(2 * time.Second) // 确保所有子协程都进入等待状态
​
cond.L.Lock()
ready = true
cond.Broadcast()
cond.L.Unlock()
​
wg4.Wait() // 等待所有子协程完成
}
​