Go goroutine做並行程序時,利用sync.RWMutex
讀寫鎖(read write lock)限制goroutine對競爭資源的讀取及寫入。
RWMutex
提供用來鎖定goroutine共享資源的讀鎖(read lock)和寫鎖(write lock)。
RWMutex
與Mutex
的差別為RWMutex
對共用資源的讀取和寫入提供讀鎖與寫鎖,讀鎖間不排斥、讀寫鎖及寫讀鎖相互排斥;而Mutex
只有一種鎖,不分讀寫互相排斥。
讀鎖 RWMutex.RLock
RWMutex.RLock
為讀鎖;RWMutex.RUnlock
為解鎖讀鎖。讀鎖允許同時由多個goroutine取得,即共用資源可同時被多個goroutine取得讀鎖,不過共用資源上讀鎖時其他goroutine無法取得寫鎖;反之亦然。
寫鎖 RWMutex.Lock
RWMutex.Lock
為寫鎖;RWMutex.Unlock
為解鎖寫鎖。寫鎖僅允許同時被一個goroutine取得,共用資源上寫鎖時其他goroutine無法取得讀鎖;反之亦然。
範例環境:
- Go 1.19
未加鎖
下面count
為被多個goroutine存取的共用資源;add()
累加count
的值;print()
印出count
的值。add()
和print()
存取count
時未加任何鎖。add()
和print()
由不同的goroutine執行。
main.go
package main
import (
"fmt"
"sync"
"time"
)
var count int = 0
var rwmu sync.RWMutex
func main() {
go add()
go print()
go print()
time.Sleep(time.Second * 1)
}
func print() {
// rwmu.RLock()
// defer rwmu.RUnlock()
fmt.Printf("count=%d\n", count)
}
func add() {
// rwmu.Lock()
// defer rwmu.Unlock()
for i := 0; i < 100000; i++ {
count++
}
fmt.Println("add finished!")
}
執行結果可能如下,在未上鎖的情況下count
的值在goroutine add()
累加結束前的過程中會被其他goroutine print()
印出。
count=4013
count=12156
add finished!
僅加RWMutex.Lock寫鎖
在goroutine add()
累加(寫入)count
時加上寫鎖RWMutex.Lock
,即無法被其他goroutine取得讀鎖或寫鎖。然而goroutine print()
取得count
未加任何鎖所以效果同上,即count
的值在goroutine add()
累加結束前的過程中仍會被goroutine print()
取得並印出。
main.go
package main
import (
"fmt"
"sync"
"time"
)
var count int = 0
var rwmu sync.RWMutex
func main() {
go add()
go print()
go print()
time.Sleep(time.Second * 1)
}
func print() {
// rwmu.RLock()
// defer rwmu.RUnlock()
fmt.Printf("count=%d\n", count)
}
func add() {
rwmu.Lock()
defer rwmu.Unlock()
for i := 0; i < 100000; i++ {
count++
}
fmt.Println("add finished!")
}
執行結果可能如下。
count=4743
count=2838
add finished!
僅加RWMutex.RLock讀鎖
結果同上,由於goroutine add()
寫入count
時未加寫鎖因此不影響goroutine print()
的讀鎖存取。
main.go
package main
import (
"fmt"
"sync"
"time"
)
var count int = 0
var rwmu sync.RWMutex
func main() {
go add()
go print()
go print()
time.Sleep(time.Second * 1)
}
func print() {
rwmu.RLock()
defer rwmu.RUnlock()
fmt.Printf("count=%d\n", count)
}
func add() {
// rwmu.Lock()
// defer rwmu.Unlock()
for i := 0; i < 100000; i++ {
count++
}
fmt.Println("add finished!")
}
執行結果可能如下。
count=5498
count=14995
add finished!
加RWMutex.RLock讀鎖及RWMutex.Lock寫鎖
在goroutine add()
寫入count
時加上寫鎖RWMutex.Lock
,即無法被其他讀鎖及寫鎖存取。goroutine print()
取得count
時加上讀鎖RWMutex.RLock
。
main.go
package main
import (
"fmt"
"sync"
"time"
)
var count int = 0
var rwmu sync.RWMutex
func main() {
go add()
go print()
go print()
time.Sleep(time.Second * 1)
}
func print() {
rwmu.RLock()
defer rwmu.RUnlock()
fmt.Printf("count=%d\n", count)
}
func add() {
rwmu.Lock()
defer rwmu.Unlock()
for i := 0; i < 100000; i++ {
count++
}
fmt.Println("add finished!")
}
執行結果可能有以下三種。
兩個goroutine print()
取得讀鎖前先等goroutineadd()
的寫鎖釋放。
add finished!
count=100000
count=100000
一個goroutine print()
先取得讀鎖印出後釋放,然後由goroutine add()
取得寫鎖累加count
值後釋放、最後由另一個goroutine print()
取得讀鎖印出。
count=0
add finished!
count=100000
兩個goroutine print()
先取得讀鎖印出後釋放,最後才由goroutine add()
取得寫鎖累加count
值。
count=0
count=0
add finished!
沒有留言:
張貼留言