一、使用方法
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()
}