在高並發下或多goroutine同時執行下,可能會同時讀寫同一塊內存
Golang樂觀鎖和悲觀鎖
修改一個數值的步驟:
①把想修改的數值從某個地方取出來
②在取出來的數值修改為期望值
③把修改后的數值保存到原來的地方
可能存在的問題:
如果兩個goroutine同時執行修改數值的步驟,都要進行第③步了,這么看下來,先執行第③步的goroutine做了白功,因為后面那個goroutine緊接着就把這個值覆蓋了
悲觀鎖認為:
每次修改變量的值的時候總會遇到並發,一定會存在其他goroutine也在修改這個變量的值。為了防止此類情況發生,一定要嚴格執行 ①先上鎖 ②執行修改的3個步驟 ③解鎖,以保證數據的精確。
悲觀鎖有互斥鎖和讀寫鎖
樂觀鎖認為:
每次修改變量值的時候不會遇到並發這么倒霉的事,所以3個步驟中直接不上鎖走完①②步,到了要執行第③步的時候,檢查一下這個數值是否改變,如果沒有,則執行第③步;如果被已經被別的goroutine修改了,那么重新執行①②③。整個環節不存在加鎖和解鎖的操作
【悲觀鎖】互斥鎖 & 讀寫鎖
互斥鎖:任何一個goroutine讀或者寫,其他goroutine都只能等待。適用於讀寫相當的場景
讀寫鎖:只要沒有goroutine寫,可以有多個goroutine一起讀。一旦有一個goroutine要開始寫,其他goroutine都不能讀和寫。適用於讀多寫少的場景
看起來讀寫鎖效率要高一些,但一般情況下卻是互斥鎖更加高效?讀寫鎖的底層實現是互斥鎖+計數器,只有在鎖住的時候業務耗時過長,讀寫沖突更嚴重的時候,讀寫鎖才有優勢
參考文章:https://blog.csdn.net/myz123321/article/details/89048002,我沒試過。
【atomic原子操作】
原子操作是指不會被線程調度機制打斷的操作;原子操作一旦開始,就一直運行到結束,中間不會有任何 context switch(上下文切換,從當前線程切換到另一個線程)。(from 百度百科)
對於編程語言中變量的修改,可以認為原子操作需滿足下面的條件:① 變量的讀寫過程不可中斷(不可以讀到一半做其他事情,也不可寫到一半做其他事情),② 變量的讀寫過程不可同時進行(不可以寫變量的同時讀取變量,防止讀取到僅更新了一半的變量值;也不可以寫變量的同時另一個線程也在寫這個變量,防止其中一個更新無效)。
import "sync/atomic" atomic.AddInt64(&p, 1000) atomic.LoadInt64(&p, 1000)
可惜只能對數值進行操作
參考文章:https://jingwei.link/2019/05/11/golang-concurrency-02.html