读写锁(Read-Write Lock)
读写锁是一种特殊的锁,它允许多个读操作并发执行,但写操作是独占的。也就是说,在同一时间,可以有多个线程同时读取共享资源,但只有一个线程可以写入共享资源,并且在写入时不允许其他线程读取。
读写锁特别适用于读多写少的场景,因为它可以提高并发性和性能。
Go中的读写锁
在Go语言中,读写锁由标准库sync包提供。常用的类型是sync.RWMutex,它有以下方法:
RLock(): 获取读锁。如果写锁已被持有,调用此方法的Goroutine会阻塞,直到写锁被释放。RUnlock(): 释放读锁。Lock(): 获取写锁。如果读锁或写锁已被持有,调用此方法的Goroutine会阻塞,直到所有锁都被释放。Unlock(): 释放写锁。
读写锁的例子
下面是一个简单的例子,展示如何在Go中使用读写锁来保护共享资源。
go
package main
import (
"fmt"
"sync"
"time"
)
var (
counter int
rwMutex sync.RWMutex
)
func readCounter(id int, wg *sync.WaitGroup) {
defer wg.Done()
rwMutex.RLock() // 获取读锁
defer rwMutex.RUnlock() // 在函数退出时释放读锁
fmt.Printf("Goroutine %d reading counter: %d\n", id, counter)
time.Sleep(100 * time.Millisecond) // 模拟读取操作
}
func writeCounter(id int, wg *sync.WaitGroup) {
defer wg.Done()
rwMutex.Lock() // 获取写锁
defer rwMutex.Unlock() // 在函数退出时释放写锁
counter++
fmt.Printf("Goroutine %d writing counter: %d\n", id, counter)
time.Sleep(100 * time.Millisecond) // 模拟写入操作
}
func main() {
var wg sync.WaitGroup
// 启动5个读操作
for i := 1; i <= 5; i++ {
wg.Add(1)
go readCounter(i, &wg)
}
// 启动2个写操作
for i := 1; i <= 2; i++ {
wg.Add(1)
go writeCounter(i, &wg)
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}代码解释
定义读写锁和共享变量:
govar ( counter int rwMutex sync.RWMutex )使用读写锁保护读操作:
gofunc readCounter(id int, wg *sync.WaitGroup) { defer wg.Done() rwMutex.RLock() // 获取读锁 defer rwMutex.RUnlock() // 在函数退出时释放读锁 fmt.Printf("Goroutine %d reading counter: %d\n", id, counter) time.Sleep(100 * time.Millisecond) // 模拟读取操作 }使用读写锁保护写操作:
gofunc writeCounter(id int, wg *sync.WaitGroup) { defer wg.Done() rwMutex.Lock() // 获取写锁 defer rwMutex.Unlock() // 在函数退出时释放写锁 counter++ fmt.Printf("Goroutine %d writing counter: %d\n", id, counter) time.Sleep(100 * time.Millisecond) // 模拟写入操作 }主函数:
gofunc main() { var wg sync.WaitGroup // 启动5个读操作 for i := 1; i <= 5; i++ { wg.Add(1) go readCounter(i, &wg) } // 启动2个写操作 for i := 1; i <= 2; i++ { wg.Add(1) go writeCounter(i, &wg) } wg.Wait() fmt.Println("Final Counter:", counter) }
在主函数中,启动了5个读Goroutine和2个写Goroutine。读Goroutine获取读锁,可以并发读取counter的值。而写Goroutine获取写锁,确保在写入时没有其他Goroutine读取或写入counter。在所有Goroutine执行完毕后,打印出最终的counter值。
小结
通过使用读写锁,可以提高读多写少场景下的并发性能。在Go语言中,使用sync.RWMutex提供的RLock、RUnlock、Lock和Unlock方法,可以方便地实现对读写操作的保护,确保数据的一致性和正确性。