Go并發編程-RWMutex
簡單用
? 讀寫互斥鎖RWMutex與互斥鎖Mutex使用方法非常類似,在使用互斥鎖Mutex時,并行會變成串行化,降低了效率,分析的時候就會發現讀寫如果能夠分離開能極大的提高效率,也就是將串行讀改為并行讀,串行寫保持不變,這是一個經典的readers-wriders問題,解決readers-writers有三種方案,read-prefer,write-prefer,不指定優先級,
-
Read-prefer:讀優先,提高很好的并發性,但是會導致寫饑餓問題,
-
Write-prefer:寫優先,當有讀請求時,新來的read請求會阻塞,解決了寫饑餓問題
-
不區分優先級:沒有優先,沒有饑餓問題
RWMutex采用了write-prefer方案,一個正在阻塞的Lock呼叫會排除新的reader請求到鎖,
type Counter struct {
mu sync.RWMutex
count int
}
func (c *Counter) Count() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.count
}
func (c *Counter) Incr() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
- 使用Mutex是不需要初始化的,在使用時會自動去初始化,所以在使用時不需要自己去創建一個物件
- Mutex變數宣告時最好宣告在其臨界變數的上面,然后使用空格把欄位分隔開來,邏輯會跟清晰,便于維護
- Lock和Unlock方法成對出現,使用defer去解鎖,邏輯會跟清晰,便于維護,防止出現漏掉的情況
看實作
type RWMutex struct {
w Mutex // 互斥鎖
writerSem uint32 // writer信號量
readerSem uint32 // reader信號量
readerCount int32 // reader的數量
readerWait int32 // writer等待完成的reader的數量
}
const rwmutexMaxReaders = 1 << 30
func (rw *RWMutex) RLock() {
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
}
func (rw *RWMutex) RUnlock() {
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
rw.rUnlockSlow(r)
}
}
func (rw *RWMutex) Lock() {
// First, resolve competition with other writers.
rw.w.Lock()
// Announce to readers there is a pending writer.
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
}
func (rw *RWMutex) Unlock() {
// Announce to readers there is no active writer.
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
}
// Unblock blocked readers, if any.
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
// Allow other writers to proceed.
rw.w.Unlock()
}
? rwmutexMaxReaders 定義了最大的reader的數量
? readerCount:不僅表示reader的數量,同時也標記著writer競爭鎖或者持有鎖的狀態
別踩坑
-
不是成對出現
要保證讀寫鎖必須成對出現,不遺漏不多余,否則都會panic
-
不可復制
與Mutex不能復制的原因相同,sync的包中的同步原語是不能復制的,因為他們是有狀態的物件,復制一個已經加鎖的RWMutex給一個新的變數明顯不符合預期,雖然這種情況很簡單,但是還是會很容易出錯,因為go的函式呼叫會自動復制,所以有關鎖的引數傳遞要使用指標的方式,
-
重入鎖
與Mutex相同,RWMutex也是不可重入鎖,由于重入鎖導致的死鎖情況有很多,
- write鎖重入呼叫Lock時發生死鎖
func foo(rwm *sync.RWMutex) { fmt.Println("in foo") rwm.Lock() bar(rwm) rwm.Unlock() } func bar(rwm *sync.RWMutex) { rwm.Lock() fmt.Println("in bar") rwm.Unlock() } func main() { var rwm sync.RWMutex foo(&rwm) }- 讀寫依賴導致死鎖
func foo(rwm *sync.RWMutex) { fmt.Println("in foo") rwm.RLock() bar(rwm) rwm.RUnlock() } func bar(rwm *sync.RWMutex) { rwm.Lock() fmt.Println("in bar") rwm.Unlock() } func main() { var rwm sync.RWMutex foo(&rwm) }- writer依賴reader. reader依賴新來的reader. 新來的reader依賴writer形成環狀
func main() { var m sync.RWMutex go func() { time.Sleep(200 * time.Millisecond) m.Lock() fmt.Println("Lock") time.Sleep(100 * time.Millisecond) m.Unlock() fmt.Println("Unlock") }() factorial(&m, 10) } func factorial(m *sync.RWMutex, n int) int { if n < 1 { return 0 } fmt.Println("RLock") m.RLock() defer func() { fmt.Println("RUnlock") m.RUnlock() }() time.Sleep(100 * time.Millisecond) return factorial(m, n-1) * n }
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/261429.html
標籤:區塊鏈
下一篇:GDB簡介
