互斥鎖簡單粗暴,誰拿到誰操作。今天給大家介紹一下讀寫鎖,讀寫鎖比互斥鎖略微復雜一些,不過我相信我們今天能夠把他拿下!
golang讀寫鎖,其特征在于
- 讀鎖:可以同時進行多個協程讀操作,不允許寫操作
- 寫鎖:只允許同時有一個協程進行寫操作,不允許其他寫操作和讀操作
讀寫鎖有兩種模式。沒錯!一種是讀模式,一種是寫模式。當他為寫模式的話,作用和互斥鎖差不多,只允許有一個協程搶到這把鎖,其他協程乖乖排隊。但是讀模式就不一樣了,他允許你多個協程讀,但是不能寫??偨Y起來就是:
- 僅讀模式: 多協程可讀不可寫
- 僅寫模式: 單協程可寫不可讀
在32位的操作系統(tǒng)中,針對int64類型的值的讀和寫操作都不可能僅由一個CPU指令來完成。如若一個寫操作剛剛執(zhí)行完第一個指令,就去進行另一個讀的協程,這樣就會讀到一個錯誤的數據。
下面看個例子吧:
先看主函數:
func main() {
for i:=0;i5;i++{
wg06.Add(1)
go write(i)
wg06.Add(1)
go read(i)
}
wg06.Wait()
}
每次開辟兩條協程,一條協程執(zhí)行寫函數,另一條執(zhí)行讀函數。然后放入等待組。共開辟五次。
在來看一看寫函數
func write(i int) {
//鎖定為僅寫模式,其他協程被阻塞
rwm.Lock()
fmt.Println(i,"writing...")
- time.After(10*time.Second)
fmt.Println("write over!")
rwm.Unlock()
//解鎖僅寫模式
wg06.Done()
}
這個Lock()就是執(zhí)行讀寫鎖的寫模式,當這個模式進行時,只有這條協程能寫,其他協程都被阻塞。Unlock()就是解鎖這個僅鎖模式,等待組中的其他協程不再被阻塞。
再看一看讀模式:
func read(i int) {
rwm.RLock()
fmt.Println(i,"reading...")
-time.After(10 * time.Second)
fmt.Println(i,"read over!")
rwm.RUnlock()
wg06.Done()
}
RLock()就是執(zhí)行讀寫鎖的讀模式,執(zhí)行這個模式其他協程也能讀,但是都不能寫。
如果程序運行,寫協程先搶到鎖,所有協程就不能讀,只有這條寫協程能寫,其他人都等著。如果是讀協程搶到鎖,所以寫協程就不可能了,但是讀協程仍然可以搶。
現在你知道我們應該什么時候使用讀寫鎖了嗎?
在并發(fā)進行讀寫操作時,當讀的次數遠遠超過寫的次數的情況下,應該使用讀寫鎖來進行讀寫并發(fā)操作。
Golang讀寫鎖底層原理
在加讀鎖和寫鎖的工程中都使用atomic.AddInt32來進行遞增,而該指令在底層是會通過LOCK來進行CPU總線加鎖的,因此多個CPU同時執(zhí)行readerCount其實只會有一個成功,從這上面看其實是寫鎖與讀鎖之間是相對公平的,誰先達到誰先被CPU調度執(zhí)行,進行LOCK鎖cache line成功,誰就加成功鎖
底層實現的CPU指令
底層的2條指令,通過LOCK指令配合CPU的MESI協議,實現可見性和內存屏障,同時通過XADDL則用來保證原子性,從而解決可見性與原子性問題
// atomic/asm_amd64.s TEXT runtime∕internal∕atomic·Xadd(SB)
LOCK
XADDL AX, 0(BX)
可見性與內存屏障、原子性, 其中可見性通常是指在cpu多級緩存下如何保證緩存的一致性,即在一個CPU上修改了了某個數據在其他的CPU上不會繼續(xù)讀取舊的數據,內存屏障通常是為了CPU為了提高流水線性能,而對指令進行重排序而來,而原子性則是指的執(zhí)行某個操作的過程的不可分割
總結
到此這篇關于Golang并發(fā)操作中常見讀寫鎖的文章就介紹到這了,更多相關Golang并發(fā)讀寫鎖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!