Go語言之讀寫鎖
前面的有篇文章在講資源競爭的時候,提到了互斥鎖。互斥鎖的根本就是當一個goroutine訪問的時候,其他goroutine都不能訪問,這樣肯定保證了資源的同步,避免了競爭,不過也降低了效能。
仔細剖析我們的場景,當我們讀取一個資料的時候,如果這個資料永遠不會被修改,那麼其實是不存在資源競爭的問題的。因為資料是不變的,不管怎麼讀取,多少goroutine同時讀取,都是可以的。
所以其實讀取並不是問題,問題主要是修改。修改的資料要同步,這樣其他goroutine才可以感知到。所以真正的互斥應該是讀取和修改、修改和修改之間,讀取和讀取是沒有互斥操作的。
所以這就延伸出來另外一種鎖,叫做讀寫鎖。
讀寫鎖可以讓多個讀操作同時併發,同時讀取,但是對於寫操作是完全互斥的。也就是說,當一個goroutine進行寫操作的時候,其他goroutine既不能進行讀操作,也不能進行寫操作。
var count int
var wg sync.WaitGroup
func main() { wg.Add(10) for i:=0;i<5;i++ { go read(i) } for i:=0;i<5;i++ { go write(i); } wg.Wait()}func read(n int) { fmt.Printf("讀goroutine %d 正在讀取...n",n) v := count fmt.Printf("讀goroutine %d 讀取結束,值為:%dn", n,v) wg.Done()
}
func write(n int) { fmt.Printf("寫goroutine %d 正在寫入...n",n) v := rand.Intn(1000) count = v fmt.Printf("寫goroutine %d 寫入結束,新值為:%dn", n,v) wg.Done()
}
以上我們定義了一個共享的資源count,並且宣告瞭兩個函式read和write進行讀寫。在main函式的測試中,我們同時啟動了 5 個讀寫goroutine進行讀寫操作,透過列印的結果來看,寫入操作是處於競爭狀態的,有的寫入操作被覆蓋了。透過go build -race也可以看到更明細的競爭態。
針對這種情況,第一個方案是加互斥鎖,同時只能有一個goroutine可以操作count。但是這種方法效能比較慢,而且我們說的讀操作可以不互斥,所以這種情況比較適合使用讀寫鎖。
var count int
var wg sync.WaitGroup
var rw sync.RWMutex
func main() { wg.Add(10) for i:=0;i<5;i++ { go read(i) } for i:=0;i<5;i++ { go write(i); } wg.Wait()}func read(n int) { rw.RLock() fmt.Printf("讀goroutine %d 正在讀取...n",n) v := count fmt.Printf("讀goroutine %d 讀取結束,值為:%dn", n,v) wg.Done() rw.RUnlock()}func write(n int) { rw.Lock() fmt.Printf("寫goroutine %d 正在寫入...n",n) v := rand.Intn(1000) count = v fmt.Printf("寫goroutine %d 寫入結束,新值為:%dn", n,v) wg.Done() rw.Unlock()
}
我們在read裡使用讀鎖,也就是RLock和RUnlock,寫鎖的方法名和我們平時使用的一樣,是Lock和Unlock。這樣,我們就使用了讀寫鎖,可以併發地讀,但是同時只能有一個寫,並且寫的時候不能進行讀操作。現在我們再執行程式碼,可以從輸出的資料看到,可以讀到新值了。
我們同時也可以使用go build -race檢測,也沒有競爭提示了。
我們在做Java開發的時候,肯定知道SynchronizedMap這個Map,它是一個在多執行緒下安全的Map,我們可以透過Collections.synchronizedMap(Map<K, V>)來獲取一個安全的Map。下面我們看看如何使用讀寫鎖,基於Go語言來實現一個安全的Map。
package common
import ( "sync")
//安全的Map
type SynchronizedMap struct { rw *sync.RWMutex data map[interface{}]interface{}
}
//儲存操作
func (sm *SynchronizedMap) Put(k,v interface{}){ sm.rw.Lock() defer sm.rw.Unlock() sm.data[k]=v
}
//獲取操作
func (sm *SynchronizedMap) Get(k interface{}) interface{}{ sm.rw.RLock() defer sm.rw.RUnlock() return sm.data[k]
}
//刪除操作
func (sm *SynchronizedMap) Delete(k interface{}) { sm.rw.Lock() defer sm.rw.Unlock() delete(sm.data,k)
}
//遍歷Map,並且把遍歷的值給回撥函式,可以讓呼叫者控制做任何事情
func (sm *SynchronizedMap) Each(cb func (interface{},interface{})){ sm.rw.RLock() defer sm.rw.RUnlock() for k, v := range sm.data { cb(k,v) }
}
//生成初始化一個SynchronizedMap
func NewSynchronizedMap() *SynchronizedMap{ return &SynchronizedMap{ rw:new(sync.RWMutex), data:make(map[interface{}]interface{}), }
}
這個安全的Map被我們定義為一個SynchronizedMap的結構體,這個結構體裡有兩個欄位,一個是讀寫鎖rw,一個是儲存資料的data,data是map型別。
然後就是給SynchronizedMap定義一些方法,如果這些方法是增刪改的,就要使用寫鎖;如果是隻讀的,就使用讀鎖。這樣就保證了我們資料data在多個goroutine下的安全性。
有了這個安全的Map我們就可以在多goroutine下增刪改查資料了,都是安全的。
這裡定義了一個Each方法,這個方法很有意思,用過Gradle的都知道,也有類似遍歷Map的方法。這個方法我們可以傳入一個回撥函式作為引數,來對我們遍歷的SynchronizedMap資料進行處理,比如我列印SynchronizedMap中的資料。
sm.Each(func(k interface{}, v interface{}) { fmt.Println(k," is ",v)
}
sm就是一個SynchronizedMap,非常簡潔吧。
以上就是讀寫鎖使用的一個例子。我們可以把這個map資料當成快取資料,或者當成資料庫,然後使用讀寫鎖進行控制,可以多讀,但是隻能有一個寫。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2001/viewspace-2817142/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Go語言中的互斥鎖和讀寫鎖(Mutex和RWMutex)GoMutex
- Go語言之methodGo
- Go語言之ContextGoContext
- Go語言之介面Go
- go語言之反射-------ReflectionGo反射
- Go語言之 Struct TagGoStruct
- 深度解密Go語言之Slice解密Go
- 深度解密 Go 語言之 channel解密Go
- 深度解密Go語言之channel解密Go
- 深度解密Go語言之context解密GoContext
- 深度解密Go語言之 map解密Go
- 深度解密 Go 語言之 context解密GoContext
- 深度解密GO語言之反射解密Go反射
- Go語言之包(package)管理GoPackage
- Go 語言讀寫 Excel 文件GoExcel
- go語言之陣列與切片Go陣列
- Go語言之旅:基本型別Go型別
- Go語言之錯誤處理Go
- Go語言之併發示例(Runner)Go
- go語言之結構體和方法Go結構體
- 深度解密 Go 語言之 sync.map解密Go
- 深度解密 Go 語言之 sync.Pool解密Go
- 讀寫鎖
- 在 Fefora 上開啟 Go 語言之旅Go
- python和GO語言之間的區別!PythonGo
- Go語言之變數逃逸(Escape Analysis)分析Go變數
- Go語言之切片(slice)快速入門篇Go
- Go語言之陣列快速入門篇Go陣列
- 【轉】使用 Go 語言讀寫 Redis 協議GoRedis協議
- 讀寫鎖 ReentrantReadWriteLock
- c語言之字串轉大寫聯絡C語言字串
- 深入理解GO語言之併發機制Go
- Go語言之對映(map)快速入門篇Go
- Go語言之Goroutine與通道、異常處理Go
- Lock鎖之重入鎖與讀寫鎖
- Java讀寫鎖ReadWriteLockJava
- Python和GO語言之間的區別是什麼?PythonGo
- Lock介面、重入鎖ReentrantLock、讀寫鎖ReentrantReadWriteLockReentrantLock