您好,登錄后才能下訂單哦!
golang中鎖的使用場景主要涉及到哪些呢,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
當程序中就一個線程的時候,是不需要加鎖的,但是通常實際的代碼不會只是單線程,有可能是多個線程同時訪問公共資源,所以這個時候就需要用到鎖了,那么關于鎖的使用場景主要涉及到哪些呢?
1. 多個線程在讀相同的數據時
2. 多個線程在寫相同的數據時
3. 同一個資源,有讀又有寫時
互斥鎖 (sync.Mutex)
讀寫鎖 (sync.RWMutex 底層依賴Mutex實現 )
互斥鎖是并發程序對公共資源訪問限制最常見的方式。在Go中,sync.Mutex 提供了互斥鎖的實現。
當一個goroutine獲得了Mutex后,其他goroutine只能等待,除非該goroutine釋放這個Mutex。
互斥鎖結構:
type Mutex struct { state int32 sema uint32}
1. 鎖定狀態值為1,未鎖定狀態鎖為 0 。
2. Lock()加鎖、Unlock解鎖。
讀寫鎖則是對讀寫操作進行加鎖。需要注意的是多個讀操作之間不存在互斥關系,這樣提高了對共享資源的訪問效率。
Go中讀寫鎖由 sync.RWMutex 提供,RWMutex在讀鎖占用的情況下,會阻止寫,但不阻止讀。RWMutex在寫鎖占用情況下,會阻止任何其他goroutine(無論讀和寫)進來,整個鎖相當于由該goroutine獨占。
讀寫鎖結構:
type RWMutex struct { w Mutex // held if there are pending writers writerSem uint32 // semaphore for writers to wait for completing readers readerSem uint32 // semaphore for readers to wait for completing writers readerCount int32 // number of pending readers readerWait int32 // number of departing readers}
1. RWMutex是單寫多讀鎖,該鎖可以加多個讀鎖或者一個寫鎖。
2. 讀鎖占用的情況會阻止寫,不會阻止讀,多個goroutine可以同時獲取讀鎖。
3. 寫鎖會阻止其他gorotine不論讀或者寫進來,整個鎖由寫鎖goroutine占用 與第一條共用示范代碼
4. 適用于讀多寫少的場景
Mutex為互斥鎖,Lock() 加鎖,Unlock() 解鎖,使用Lock() 加鎖后,便不能再次對其進行加鎖,直到利用Unlock()解鎖對其解鎖后,才能再次加鎖.適用于讀寫不確定場景,即讀寫次數沒有明顯的區別,并且只允許只有一個讀或者寫的場景,所以該鎖葉叫做全局鎖。
互斥鎖只能鎖定一次,當在解鎖之前再次進行加鎖,便會無法加鎖。如果在加鎖前解鎖,便會報錯"panic: sync: unlock of unlocked mutex"。
package main
import ("fmt"
"sync"
)
var (
count int
lock sync.Mutex
)
func main() {
for i := 0; i < 2; i++ {
go func() {
for i := 1000000; i > 0; i-- {
lock.Lock()
count ++
lock.Unlock()
}
fmt.Println(count)
}()
}
fmt.Scanf("\n") //等待子線程全部結束
}
運行結果:
1952533
2000000 //最后的線程打印輸出
對于上面的程序,a作為一個公共的資源,所以對a的改變、讀寫等操作都需要加鎖。
需要注意的問題:
1. 不要重復鎖定互斥鎖
2. 不要忘記解鎖互斥鎖,必要時使用 defer 語句
3. 不要在多個函數之間直接傳遞互斥鎖
讀寫鎖的場景主要是在多線程的安全操作下,并且讀的情況多于寫的情況,也就是說既滿足多線程操作的安全性,也要確保性能不能太差,這時候,我們可以考慮使用讀寫鎖。當然你也可以簡單暴力直接使用互斥鎖(Mutex)。
Lock() 寫鎖,如果在添加寫鎖之前已經有其他的讀鎖和寫鎖,則lock就會阻塞直到該鎖可用,為確保該鎖最終可用,已阻塞的 Lock 調用會從獲得的鎖中排除新的讀取器,即寫鎖權限高于讀鎖,有寫鎖時優先進行寫鎖定。
Unlock() 寫鎖解鎖,如果沒有進行寫鎖定,則就會引起一個運行時錯誤。
RLock() 讀鎖,當有寫鎖時,無法加載讀鎖,當只有讀鎖或者沒有鎖時,可以加載讀鎖,讀鎖可以加載多個,所以適用于"讀多寫少"的場景。
RUnlock() 讀鎖解鎖,RUnlock 撤銷單次RLock 調用,它對于其它同時存在的讀取器則沒有效果。若 rw 并沒有為讀取而鎖定,調用 RUnlock 就會引發一個運行時錯誤。
package main
import ("fmt"
"sync"
)
var (
count int
rwLock sync.RWMutex
)
func main() {
for i := 0; i < 2; i++ {
go func() {
for i := 1000000; i > 0; i-- {
rwLock.Lock()
count ++
rwLock.Unlock()
}
fmt.Println(count)
}()
}
fmt.Scanf("\n") //等待子線程全部結束
}
運行結果:
1968637
2000000
看著挺復雜的,其實簡單來說就是:
讀鎖不能阻塞讀鎖
讀鎖需要阻塞寫鎖,直到所有讀鎖都釋放
寫鎖需要阻塞讀鎖,直到所有寫鎖都釋放
寫鎖需要阻塞寫鎖
關于golang中鎖的使用場景主要涉及到哪些呢問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。