Go 互斥鎖和讀寫互斥鎖的實現

先來看這樣一段代碼,所存在的問題:

var wg sync.WaitGroup
var x int64

func main() {
 wg.Add(2)
 go f()
 go f()
 wg.Wait()
 fmt.Println(x) // 輸出:12135
}

func f()  {
 for i:=0;i<10000;i++ {
  x = x+1
 }
 wg.Done() 
}

這裡為什麼輸出是 12135(不同的機器結果不一樣),而不是20000。

因為 x 的賦值,總共分為三個步驟:取出x的值、計算x的結果、給x賦值。那麼由於對於f函數的調用采用瞭goroutine協程,所以存在資源競爭的問題,所以有賦值和計算過程中存在臟數據。對於此類的問題,可以采用互斥鎖解決:

互斥鎖

互斥鎖是一種常用的控制共享資源訪問的方法,它能夠保證同時隻有一個goroutine可以訪問共享資源。Go語言中使用sync包的Mutex類型來實現互斥鎖。

var wg sync.WaitGroup
var x int64
var lock sync.Mutex

func main() {
 wg.Add(2)
 go f()
 go f()
 wg.Wait()
 fmt.Println(x) // 輸出:20000
}

func f()  {
 for i:=0;i<10000;i++ {
  lock.Lock() // 加互斥鎖
  x = x+1
  lock.Unlock() // 解鎖
 }
 wg.Done() 
}

使用互斥鎖能夠保證同一時間有且隻有一個goroutine進入臨界區,其他的goroutine則在等待鎖;當互斥鎖釋放後,等待的goroutine才可以獲取鎖進入臨界區,多個goroutine同時等待一個鎖時,喚醒的策略是隨機的。

讀寫互斥鎖

互斥鎖是完全互斥的,但是有很多實際的場景下是讀多寫少的,當我們並發的去讀取一個資源不涉及資源修改的時候是沒有必要加鎖的,這種場景下使用讀寫鎖是更好的一種選擇。讀寫鎖在Go語言中使用sync包中的RWMutex類型。

讀寫鎖分為兩種:讀鎖和寫鎖。當一個goroutine獲取讀鎖之後,其他的goroutine如果是獲取讀鎖會繼續獲得鎖,如果是獲取寫鎖就會等待;當一個goroutine獲取寫鎖之後,其他的goroutine無論是獲取讀鎖還是寫鎖都會等待。

var (
 x1 int64
 wg1 sync.WaitGroup
 lock1 sync.Mutex
 rwlock sync.RWMutex
)

func main() {
 startTime := time.Now()
 for i:=0;i<100;i++ {
  wg1.Add(1)
  write()
 }
 for i:=0;i<10000;i++ {
  wg1.Add(1)
  read()
 }
 wg1.Wait()
 fmt.Println(time.Now().Sub(startTime))

 // 互斥鎖用時: 973.9304ms
 // 讀寫互斥鎖用時: 718.094ms
}

func read()  {
 defer wg1.Done()
 //lock1.Lock() // 互斥鎖
 rwlock.RLock() // 讀寫互斥鎖
 fmt.Println(x1)
 //lock1.Unlock() // 互斥鎖
 rwlock.RUnlock() // 讀寫互斥鎖
}

func write()  {
 defer wg1.Done()
 lock1.Lock()
 x1 = x1+1
 lock1.Unlock()
}

到此這篇關於Go 互斥鎖和讀寫互斥鎖的實現的文章就介紹到這瞭,更多相關Go 互斥鎖和讀寫互斥鎖內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: