1. 程式人生 > 其它 >golang 互斥鎖 讀寫鎖

golang 互斥鎖 讀寫鎖

併發場景,鎖機制尤為關鍵,我們一起通俗易懂的瞭解下golang的鎖吧

一 為什麼加鎖?

  Go是多執行緒的,存在多個goroutine同時操作一個資源(臨界區),這種情況會發生競態問題(資料競態)。類比火車上的衛生間被車廂裡的人競爭。

  這裡注意一點:

    go裡的鎖,並非說給火車的衛生間設定一個門鎖,而是給各個乘客發放一個對講機。乘客先詢問對講機能否使用衛生間,若通過則直接去,不通過則一直等待,知道對講機另一邊通過了。

    所以這樣我們就可以理解為,倘若乘客x未被髮放對講機,那麼他要是用衛生間,則直接前去就好。

    那麼這樣有個問題,當x使用衛生間的時候,其他有對講機並通過的乘客前往,這就造成了競態問題。如何解決?---------------------> 很明顯,不要遺漏給x發放對講機!

    (當然,另一種解決方案就是設定一個門鎖,但是golang的機制不是這樣的,這點很關鍵)

二 sync.Mutex互斥鎖

  互斥鎖能夠保證同一時間有且只有一個goroutine進入臨界區,其他的goroutine則在等待鎖;

  當互斥鎖釋放後,等待的goroutine才可以獲取鎖進入臨界區,多個goroutine同時等待一個鎖時,喚醒的策略是隨機的。
  (我一加鎖,其他人啥都別幹,等著吧)

  

func main(){
    var lock sync.Mutex
    go func() {
        fmt.Println("乘客1詢問對講機能否使用衛生間 at 
"+ time.Now().String()) lock.Lock() defer lock.Unlock() fmt.Println("乘客1開始 at "+ time.Now().String()) time.Sleep(time.Second * 2) fmt.Println("乘客1結束 at "+ time.Now().String()) }() time.Sleep(time.Second/10) go func() { fmt.Println("乘客2詢問對講機能否使用衛生間 at
"+ time.Now().String()) lock.Lock() defer lock.Unlock() fmt.Println("乘客2開始 at "+ time.Now().String()) time.Sleep(time.Second * 2) fmt.Println("乘客2結束 at "+ time.Now().String()) }() go func() { fmt.Println("乘客3(無對講機)直接使用 "+ time.Now().String()) time.Sleep(time.Second * 2) fmt.Println("乘客3(無對講機)結束 "+ time.Now().String()) }() //fmt.Println("main run "+ time.Now().String()) time.Sleep(time.Second * 15) }

  

  我們看到 31s 的時候,1 2 3 同時想使用衛生間, 1先獲取到對講機的許可權,開始使用,2就只能等待,而3沒有對講機,那麼他就直接使用了。
  33s的時候, 1 和 3 使用完畢,2得到了對講機的許可權,開始使用。

  35s的時候,2 使用結束。

  (要處理3和1同時使用的問題,就要給3也加個對講機,也就是程式碼中上個鎖)

三 sync.RWMutex讀寫互斥鎖

  讀寫鎖分為兩種:讀鎖和寫鎖。

  當一個goroutine獲取讀鎖之後,其他的goroutine如果是獲取讀鎖會繼續獲得鎖,如果是獲取寫鎖就會等待;

  當一個goroutine獲取寫鎖之後,其他的goroutine無論是獲取讀鎖還是寫鎖都會等待。

  • 讀鎖RLock() ,我開始讀了,你們也可以來讀,但是誰都別寫
  • 寫鎖Lock() ,我要寫了,所有人注意,別做任何操作

  

func main(){
    var rwLock sync.RWMutex
    fmt.Println("begin  "  +  time.Now().String())
    // 獲取寫鎖
    for i := 0 ; i < 5; i++{
        go func(i int) {
            rwLock.Lock()
            defer rwLock.Unlock()
            fmt.Println("write func " + strconv.Itoa(i) +" get wlock at " +  time.Now().String())
            time.Sleep(time.Second)
        }(i)
    }// 獲取讀鎖
    for i := 0 ; i < 5 ;i ++{
        go func(i int) {
            rwLock.RLock()
            defer rwLock.RUnlock()
            fmt.Println("read func " + strconv.Itoa(i) +" get rlock at " +  time.Now().String())
            time.Sleep(time.Second)
        }(i)
    }
    //散人
    for i := 0 ; i < 5 ;i ++{
        go func(i int) {
            fmt.Println("nolocker func " + strconv.Itoa(i) +" get rlock at " +  time.Now().String())
            time.Sleep(time.Second)
        }(i)
    }

    // 保證所有的 goroutine 執行結束
    time.Sleep(time.Second * 10)
}

  我們看到 58s 的時候,write0 獲取了寫鎖(其他人啥事別幹),此時read動彈不了,不過散人nolocker那五位牛人可以把事情做完(因為他沒有對講機)
  59s的時候,write0 的事情幹完了,對講機通知read0開始幹活,1幹活的時候上了讀鎖(其他reader可以幹活,writer就別動彈了),所以59s的時候,read0~read4開始幹活

  60s時,read0~read4全都幹完活,釋放了他們的讀鎖,那麼write鎖可以動彈了,對講機隨機喊來了write1來幹活,write1加了寫鎖,直到幹完活釋放

  61s也就是1s的時候write2開始幹活,依次執行,所有write幹完

  本篇到此結束!!!