溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

golang中鎖的使用場(chǎng)景主要涉及到哪些呢

發(fā)布時(shí)間:2022-01-04 17:17:24 來源:億速云 閱讀:114 作者:柒染 欄目:大數(shù)據(jù)

golang中鎖的使用場(chǎng)景主要涉及到哪些呢,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。

一、什么場(chǎng)景下需要用到鎖

當(dāng)程序中就一個(gè)線程的時(shí)候,是不需要加鎖的,但是通常實(shí)際的代碼不會(huì)只是單線程,有可能是多個(gè)線程同時(shí)訪問公共資源,所以這個(gè)時(shí)候就需要用到鎖了,那么關(guān)于鎖的使用場(chǎng)景主要涉及到哪些呢?

1. 多個(gè)線程在讀相同的數(shù)據(jù)時(shí)
2. 多個(gè)線程在寫相同的數(shù)據(jù)時(shí)
3. 同一個(gè)資源,有讀又有寫時(shí)

二、Go 鎖分為兩種:

  • 互斥鎖 (sync.Mutex)

  • 讀寫鎖 (sync.RWMutex 底層依賴Mutex實(shí)現(xiàn)  )

互斥鎖是并發(fā)程序?qū)操Y源訪問限制最常見的方式。在Go中,sync.Mutex 提供了互斥鎖的實(shí)現(xiàn)。

當(dāng)一個(gè)goroutine獲得了Mutex后,其他goroutine只能等待,除非該goroutine釋放這個(gè)Mutex。

互斥鎖結(jié)構(gòu):

type Mutex struct {    state int32    sema  uint32}

1. 鎖定狀態(tài)值為1,未鎖定狀態(tài)鎖為 0 。

2. Lock()加鎖、Unlock解鎖。

讀寫鎖則是對(duì)讀寫操作進(jìn)行加鎖。需要注意的是多個(gè)讀操作之間不存在互斥關(guān)系,這樣提高了對(duì)共享資源的訪問效率。

Go中讀寫鎖由 sync.RWMutex 提供,RWMutex在讀鎖占用的情況下,會(huì)阻止寫,但不阻止讀。RWMutex在寫鎖占用情況下,會(huì)阻止任何其他goroutine(無論讀和寫)進(jìn)來,整個(gè)鎖相當(dāng)于由該goroutine獨(dú)占。

讀寫鎖結(jié)構(gòu):

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是單寫多讀鎖,該鎖可以加多個(gè)讀鎖或者一個(gè)寫鎖。

2. 讀鎖占用的情況會(huì)阻止寫,不會(huì)阻止讀,多個(gè)goroutine可以同時(shí)獲取讀鎖。

3. 寫鎖會(huì)阻止其他gorotine不論讀或者寫進(jìn)來,整個(gè)鎖由寫鎖goroutine占用 與第一條共用示范代碼

4. 適用于讀多寫少的場(chǎng)景


三、如何使用互斥鎖

Mutex為互斥鎖,Lock() 加鎖,Unlock() 解鎖,使用Lock() 加鎖后,便不能再次對(duì)其進(jìn)行加鎖,直到利用Unlock()解鎖對(duì)其解鎖后,才能再次加鎖.適用于讀寫不確定場(chǎng)景,即讀寫次數(shù)沒有明顯的區(qū)別,并且只允許只有一個(gè)讀或者寫的場(chǎng)景,所以該鎖葉叫做全局鎖。

互斥鎖只能鎖定一次,當(dāng)在解鎖之前再次進(jìn)行加鎖,便會(huì)無法加鎖。如果在加鎖前解鎖,便會(huì)報(bào)錯(cuò)"panic: sync: unlock of unlocked mutex"。 

package mainimport ("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") //等待子線程全部結(jié)束}
運(yùn)行結(jié)果:19525332000000 //最后的線程打印輸出

對(duì)于上面的程序,a作為一個(gè)公共的資源,所以對(duì)a的改變、讀寫等操作都需要加鎖。

需要注意的問題:

  1. 不要重復(fù)鎖定互斥鎖
  2. 不要忘記解鎖互斥鎖,必要時(shí)使用 defer 語句
  3. 不要在多個(gè)函數(shù)之間直接傳遞互斥鎖

四、如何使用讀寫鎖

讀寫鎖的場(chǎng)景主要是在多線程的安全操作下,并且讀的情況多于寫的情況,也就是說既滿足多線程操作的安全性,也要確保性能不能太差,這時(shí)候,我們可以考慮使用讀寫鎖。當(dāng)然你也可以簡(jiǎn)單暴力直接使用互斥鎖(Mutex)。

Lock() 寫鎖,如果在添加寫鎖之前已經(jīng)有其他的讀鎖和寫鎖,則lock就會(huì)阻塞直到該鎖可用,為確保該鎖最終可用,已阻塞的 Lock 調(diào)用會(huì)從獲得的鎖中排除新的讀取器,即寫鎖權(quán)限高于讀鎖,有寫鎖時(shí)優(yōu)先進(jìn)行寫鎖定。

Unlock() 寫鎖解鎖,如果沒有進(jìn)行寫鎖定,則就會(huì)引起一個(gè)運(yùn)行時(shí)錯(cuò)誤。

RLock() 讀鎖,當(dāng)有寫鎖時(shí),無法加載讀鎖,當(dāng)只有讀鎖或者沒有鎖時(shí),可以加載讀鎖,讀鎖可以加載多個(gè),所以適用于"讀多寫少"的場(chǎng)景。

RUnlock() 讀鎖解鎖,RUnlock 撤銷單次RLock 調(diào)用,它對(duì)于其它同時(shí)存在的讀取器則沒有效果。若 rw 并沒有為讀取而鎖定,調(diào)用 RUnlock 就會(huì)引發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。

package mainimport ("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") //等待子線程全部結(jié)束}
運(yùn)行結(jié)果:19686372000000

看著挺復(fù)雜的,其實(shí)簡(jiǎn)單來說就是:

  1. 讀鎖不能阻塞讀鎖

  2. 讀鎖需要阻塞寫鎖,直到所有讀鎖都釋放

  3. 寫鎖需要阻塞讀鎖,直到所有寫鎖都釋放

  4. 寫鎖需要阻塞寫鎖

關(guān)于golang中鎖的使用場(chǎng)景主要涉及到哪些呢問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI