Go语言并发编程中 RWMutex 是如何实现的
Go语言并发编程中 RWMutex 是如何实现的
通过记录 readerCount 读锁的数量来进行控制,当有一个写锁的时候,会将读 锁数量设置为负数 1<<30。目的是让新进入的读锁等待之前的写锁释放通知读 锁。同样的当有写锁进行抢占时,也会等待之前的读锁都释放完毕,才会开始
进行后续的操作。 而等写锁释放完之后,会将值重新加上 1<<30, 并通知刚才 新进入的读锁(rw.readerSem),两者互相限制。
1 回复
在Go语言中,sync.RWMutex
是一种读写互斥锁,它允许对共享资源的并发访问,同时优化了读操作的性能。RWMutex
通过允许多个goroutine同时进行读操作,但写操作会阻塞其他读操作和写操作,来减少锁竞争,从而提高并发性能。
实现原理
RWMutex
的实现主要依赖于内部的两个互斥锁(通常是一个互斥锁用于写操作和一个信号量或计数器用于读操作)以及一个计数器来跟踪当前有多少个goroutine正在读取资源。
- 写锁:当请求写锁时,如果已经有读锁或写锁被持有,请求将阻塞,直到所有读锁和写锁都被释放。
- 读锁:当请求读锁时,如果有写锁被持有,请求将阻塞。但如果有其他读锁正在被持有,新的读锁请求将被允许,同时读锁计数器会增加。
示例代码
以下是一个使用 sync.RWMutex
的简单示例,演示了如何在Go中利用它来保护共享资源的读写操作。
package main
import (
"fmt"
"sync"
"time"
)
var (
data = "initial data"
rwMutex sync.RWMutex
)
func readData(id int) {
rwMutex.RLock() // 加读锁
defer rwMutex.RUnlock() // 释放读锁
fmt.Printf("Goroutine %d: %s\n", id, data)
}
func writeData(id int, newData string) {
rwMutex.Lock() // 加写锁
defer rwMutex.Unlock() // 释放写锁
fmt.Printf("Goroutine %d: Writing %s\n", id, newData)
data = newData
}
func main() {
// 启动多个goroutine读取数据
for i := 1; i <= 3; i++ {
go readData(i)
}
// 等待一会儿,确保读操作先开始
time.Sleep(100 * time.Millisecond)
// 启动写操作
go writeData(4, "new data")
// 等待足够的时间以查看输出
time.Sleep(500 * time.Millisecond)
}
// 输出示例(顺序可能不同)
// Goroutine 1: initial data
// Goroutine 2: initial data
// Goroutine 4: Writing new data
// Goroutine 3: initial data
// 注意:实际输出中,写操作可能会阻塞读操作(如果写操作发生在读操作之后请求锁),但在这个例子中,由于写操作在多数读操作之后启动,所以读操作能够先完成。
注意
- 在实际应用中,写操作的执行时间应该尽可能短,以避免长时间阻塞读操作。
- 读写锁非常适合读多写少的场景,但如果写操作非常频繁,可能会导致读操作频繁阻塞,反而降低性能。
- 使用
RWMutex
时,要确保锁的粒度适当,避免过度锁定或锁定不足。