Golang中的鎖機制主要包含互斥鎖和讀寫鎖
互斥鎖
互斥鎖是一種簡單的加鎖的方法來控制對共享資源的訪問,互斥鎖只有兩種狀態(tài),即上鎖( lock )和解鎖( unlock )路狮。
func mutex() {
var mu
sync.Mutex mu.Lock()
fmt.Println("locked")
mu.Unlock()
}
defer實現(xiàn)
func mutex() {
var mu
sync.Mutex mu.Lock()
defer mu.Unlock()
fmt.Println("locked")
}
【互斥鎖的特點】:
原子性:把一個互斥量鎖定為一個原子操作僵驰,這意味著操作系統(tǒng)(或pthread函數(shù)庫)保證了如果一個線程鎖定了一個互斥量,沒有其他線程在同一時間可以成功鎖定這個互斥量几蜻;
唯一性:如果一個線程鎖定了一個互斥量千康,在它解除鎖定之前嫩痰,沒有其他線程可以鎖定這個互斥量钞艇;
非繁忙等待:如果一個線程已經(jīng)鎖定了一個互斥量啄寡,第二個線程又試圖去鎖定這個互斥量,則第二個線程將被掛起(不占用任何cpu資源)哩照,直到第一個線程解除對這個互斥量的鎖定為止挺物,第二個線程則被喚醒并繼續(xù)執(zhí)行,同時鎖定這個互斥量飘弧。
互斥鎖是開箱即用的识藤,只需要申明sync.Mutex即可直接使用
互斥鎖應(yīng)該是成對出現(xiàn),在同步語句不可以再對鎖加鎖眯牧,看下面的示例:
func mutex() {
var mu
sync.Mutex mu.Lock()
fmt.Println("parent locked")
mu.Lock()
fmt.Println("sub locked")
mu.Unlock()
mu.Unlock()
}
此時則會出現(xiàn)fatal error: all goroutines are asleep - deadlock!錯誤
讀寫鎖
讀寫鎖和互斥鎖不同之處在于蹋岩,可以分別針對讀操作和寫操作進行分別鎖定赖草,這樣對于性能有一定的提升学少。 讀寫鎖,對于多個寫操作秧骑,以及寫操作和讀操作之前都是互斥的這一點基本等同于互斥鎖版确。 但是對于同時多個讀操作之前卻非互斥關(guān)系,這也是相讀寫鎖性能高于互斥鎖的主要原因乎折。
- 特點
開箱即用
var rwm = sync.RWMutex
- 寫鎖定和寫解鎖
rwm.Lock()
rwm.Unlock() - 讀鎖定和讀解鎖
rwm.RLock()
rwm.RUnlock() - 讀寫鎖的讀鎖和寫鎖不能交叉相互解鎖绒疗,否則會發(fā)生panic
func rwMutex() {
var rwm sync.RWMutex
rwm.Lock()
fmt.Println("locked")
rwm.RUnlock()
}
- 對于讀寫鎖,同一資源可以同時有多個讀鎖定
在goroutine中骂澄,寫解鎖會試圖喚醒所有想要進行讀鎖定而被阻塞的goroutine吓蘑。
而讀解鎖會在已無任何讀鎖定的情況下,試圖喚醒一個想進行寫鎖定而被阻塞的goroutine。
func rwMutex() {
var rwm sync.RWMutex
for i := 0; i <= 2; i++ {
go func(i int) {
fmt.Printf("go(%d) start lock\n", i)
rwm.RLock()
fmt.Printf("go(%d) locked\n", i)
time.Sleep(time.Second * 2)
rwm.RUnlock()
fmt.Printf("go(%d) unlock\n", i)
}(i)
}
// 先sleep一小會磨镶,保證for的goroutine都會執(zhí)行
time.Sleep(time.Microsecond * 100)
fmt.Println("main start lock")
// 當(dāng)子進程都執(zhí)行時溃蔫,且子進程所有的資源都已經(jīng)Unlock了
// 父進程才會執(zhí)行
rwm.Lock()
fmt.Println("main locked")
time.Sleep(time.Second)
rwm.Unlock()
}
參考
互斥鎖、條件鎖琳猫、讀寫鎖以及自旋鎖:https://www.zhihu.com/question/66733477
https://www.cnblogs.com/cangqinglang/p/13328217.html
https://laravelacademy.org/post/19901.html