Golang 基礎值速學之二十一(讀寫鎖互斥鎖)

huxiaobai_001發表於2020-07-28

我們都知道golang當中協程是很牛逼的,也是它的強項,比如你起來20個協程同時去處理一個資料,那麼這20個協程之間有可能會出現2個或者多個協程同時操作一個資料的問題,那麼就容易造成資料的錯誤!為了保證資料的絕對正確,所以引出來了互斥鎖!
我們下來看一下案例,上程式碼:

package main
func main(){
    for r := 0; r<20; r++{
       wg.Add(1)
       go test111()
    }
    wg.Wait()
}

var count int
var wg sync.WaitGroup
var mutex sync.Mutex
func test111(){
  //mutex.Lock()
  count++
  fmt.Println("the count is :",count)
  time.Sleep(time.Millisecond)
  //mutex.Unlock()
  wg.Done()
}

找到該檔案所在位置然後在dos命令列執行:

go build -race aa.go

然後就會生成一個aa.exe檔案
這個時候你再執行

aa.exe

Golang 基礎值速學之二十一(讀寫鎖互斥鎖)

Golang 基礎值速學之二十一(讀寫鎖互斥鎖)

這樣就可以看到內部的競爭關係 發現9 1 2 6 7 3 4 都存在競爭關係!
這樣是不是很清楚啦!
尤其是在高流量大併發的情況下 如果不利用互斥鎖那麼很容易就會造成多個協程同時對一個資料爭搶的現象發生!
所以呢我們需要研究研究互斥鎖

互斥鎖是傳統併發程式設計當中對共享資源進行訪問控制的主要手段,它由標準庫sync中的Mutex結構體型別表示。sync.Mutex型別只有兩個公開的指標方法,Lock和Unlock。Lock鎖定當前的共享資源,Unlock進行解鎖

看程式碼:

package main
func main(){
    for r := 0; r<20; r++{
       wg.Add(1)
       go test111()
    }
    wg.Wait()
}

var count int
var wg sync.WaitGroup
var mutex sync.Mutex
func test111(){
    //因為起來了20個協程 這20個協程之間一定會存在競爭關係 都會去改變count的值 所以容易造成錯誤的發生 加上了互斥鎖之後 一個 協程鎖定了那麼其他協程無法操作 只有等到當前協程解鎖之後才能繼續操作!
    //加鎖
    mutex.Lock()
  count++
  fmt.Println("the count is :",count)
  time.Sleep(time.Millisecond)
  //解鎖
  mutex.Unlock()
  wg.Done()
}

然後再執行

go build -race aa.go
aa.exe

你會發現就不存在競爭的關係了!
一旦鎖定那麼其他協程只能等待當前協程結束才能繼續對count進行操作!
互斥鎖一般會用到寫上比如運算元據庫的時候只能每次寫入一條那麼就可以用互斥鎖,在秒殺場景當中也是會用到互斥鎖!
互斥鎖的本質是當一個協程訪問的時候,其他協程都不能訪問,這樣在資源同步避免競爭的同時也降低了程式的併發效能,程式由原來的並行執行變成了序列執行!

其實,當我們對一個不會變化的資料只做讀操作的話是不存在資源競爭的問題的,因為資料是不變的,不管怎麼讀取多少協程同時讀取都是可以的,所以問題不是出在讀上,主要是修改,也就是寫,修改的資料要同步,這樣其他協程才可以感知到,所以真正互斥應該是讀取和修改,修改和修改之間,讀和讀是沒有互斥操作的必要的!

因此衍生出另外一種鎖,叫做讀寫鎖!

讀寫鎖可以讓多個讀操作併發,同時讀取!但是對於寫操作是完全互斥的。也就是說,當一個協程進行寫操作的時候,其他協程既不能進行讀操作也不能進行寫操作!

go當中的讀寫鎖由結構體型別sync.REMutex表示

本作品採用《CC 協議》,轉載必須註明作者和本文連結

胡軍

相關文章