Go併發編程之美-讀寫鎖

一、前言

go語言類似Java JUC包也提供了一些列用於多線程之間進行同步的措施,比如低級的同步措施有 鎖、CAS、原子變量操作類。相比Java來說go提供了獨特的基於通道的同步措施。本節我們先來看看go中讀寫鎖

二、讀寫鎖

go中讀寫鎖,在沒有線程獲取寫鎖情況下多個線程可以同時獲取讀鎖,讀鎖是可重入鎖,寫鎖則是互斥鎖(不可重入)。

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	counter int            //計數器
	wg      sync.WaitGroup //信號量
	lock    sync.RWMutex   //讀寫鎖
)

func main() {

	//1.兩個信號
	wg.Add(1)

	//2.獲取讀鎖
	fmt.Println("main thread wait rlock")
	lock.RLock()
	fmt.Println("main thread got rlock")
	//3.開啓子線程
	go incCounter()
	fmt.Println(counter)
	time.Sleep(time.Second * 5)
	//4.釋放讀鎖
	lock.RUnlock()
	fmt.Println("main thread release rlock")

	//5.等待子線程結束
	wg.Wait()
	fmt.Println(counter)

}

func incCounter() {
	defer wg.Done()

	//2.1.獲取鎖
	fmt.Println("sub thread wait rlock")
	lock.Lock()
	fmt.Println("sub thread got rlock")

	//2.2.計數加1
	counter++

	//2.3.釋放獨佔鎖
	lock.Unlock()
	fmt.Println("sub thread relese rlock")

}

  • 如上代碼go中使用sync.RWMutex可以獲取一個開箱即用的讀寫鎖
  • 代碼(2)主線程使用lock.RLock()獲取讀鎖,然後開啓了子線程(代碼3),然後在主線程持有讀鎖的情況下休眠了5s後釋放了鎖。
  • 子線程在代碼2.1嘗試使用 lock.Lock()獲取寫鎖,由於主線程還沒釋放讀鎖,所以子線程阻塞到了這裏,直到主線程休眠後執行代碼4釋放了讀鎖。
  • 執行代碼會輸出:
main thread wait rlock
main thread got rlock
0
sub thread wait rlock
main thread release rlock
sub thread got rlock
sub thread relese rlock
1

這個例子說明了,當有線程獲取了讀鎖並沒釋放時候,獲取寫鎖的線程要等待。

另外使用下面方法可以驗證讀鎖是可重入鎖:

	lock.RLock()
	lock.RLock()
	fmt.Println(counter)
	lock.RUnlock()
	lock.RUnlock()

使用下面代碼可以驗證同一個線程的讀鎖,不能晉升爲寫鎖:

	lock.RLock()
	lock.Lock()
	fmt.Println(counter)
	lock.Unlock()
	lock.RUnlock()

上面代碼執行會報錯:
image.png

三、總結

go中讀寫鎖中的讀鎖是可重入共享鎖,寫鎖是互斥鎖,讀鎖不能晉升爲寫鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章