Go語言的互斥鎖Mutex

雪山飛豬發表於2020-11-02

一、使用方法

Mutext是互斥鎖的意思,也叫排他鎖,同一時刻一段程式碼只能被一個執行緒執行,兩個方法Lock(加鎖)和Unlock(解鎖)

package main

import (
    "fmt"
    "sync"
)

func main() {
    var count = 0
    var wg sync.WaitGroup
    //十個協程數量
    n := 10
    wg.Add(n)
    for i := 0; i < n; i++ {
        go func() {
            defer wg.Done()
            //1萬疊加
            for j := 0; j < 10000; j++ {
                count++
            }
        }()
    }
    wg.Wait()
    fmt.Println(count)
}

執行結果如下

38532

新增鎖

package main

import (
    "fmt"
    "sync"
)

func main() {
    var count = 0
    var wg sync.WaitGroup
    var mu sync.Mutex
    //十個協程數量
    n := 10
    wg.Add(n)
    for i := 0; i < n; i++ {
        go func() {
            defer wg.Done()
            //1萬疊加
            for j := 0; j < 10000; j++ {
                mu.Lock()
                count++
                mu.Unlock()
            }
        }()
    }
    wg.Wait()
    fmt.Println(count)
}

執行結果如下,可以看到,已經看到結果變成了正確的100000

二、死鎖場景

當兩個或兩個以上的程式在執行過程中,因爭奪資源而處理一種互相等待的狀態,如果沒有外部干涉無法繼續下去,這時我們稱系統處於死鎖或產生了死鎖

1.Lock/Unlock不是成對出現

良好的程式設計習慣應該是讓mu.Lock()和 defer mu.Unlock()成對出現,能有效的降低死鎖出現的概率,比如忘了Lock或忘了Unlock,程式碼建議以下面緊湊的方式出現

mu.Lock()
defer mu.Unlock()

2.鎖被拷貝使用

package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    mu.Lock()
    defer mu.Unlock()
    copyTest(mu)
}

//這裡複製了一個鎖,造成了死鎖
func copyTest(mu sync.Mutex) {
    mu.Lock()
    defer mu.Unlock()
    fmt.Println("ok")
}

在函式外層已經加了一個Lock,在拷貝的時候又執行了一次Lock,因此這是一個永遠不會獲得的死鎖

3.迴圈等待

A等待B,B等待C,C等待A,陷入了無限迴圈(哲學家就餐問題)

package main

import (
    "sync"
)

func main() {
    var muA, muB sync.Mutex
    var wg sync.WaitGroup

    wg.Add(2)
    go func() {
        defer wg.Done()
        muA.Lock()
        defer muA.Unlock()
        //A依賴B
        muB.Lock()
        defer muB.Lock()
    }()

    go func() {
        defer wg.Done()
        muB.Lock()
        defer muB.Lock()
        //B依賴A
        muA.Lock()
        defer muA.Unlock()
    }()
    wg.Wait()
}

相關文章