跳到主要内容

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() // 等待所有子协程完成
}