原子操作&普通鎖&讀寫鎖
一:原子操作CAS(compare-and-swap)
原子操作分三步:讀取addr的值,和old進(jìn)行比較,如果相等,則將new賦值給*addr,他能保證這三步一起執(zhí)行完成,叫原子操作也就是說它不能再分了,當(dāng)有一個(gè)CPU在訪問這塊內(nèi)容addr時(shí),其他CPU就不能訪問
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool) TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-25 MOVD addr+0(FP), R3 MOVD old+8(FP), R4 MOVD new+16(FP), R5 SYNC LDAR (R3), R6 CMP R6, R4 BNE 7(PC) STDCCC R5, (R3) BNE -4(PC) ISYNC MOVD $1, R3 MOVB R3, swapped+24(FP) RET MOVB R0, swapped+24(FP) RET
二:普通鎖
加鎖(Mutex.Lock)
1:原子操作加鎖:原子操作判斷是否已經(jīng)被加鎖,如果沒有加鎖,原子操作加鎖,直接返回,很快嗎!
2:執(zhí)行旋轉(zhuǎn)鎖:已經(jīng)被加鎖,判斷是否可以執(zhí)行旋轉(zhuǎn)鎖,執(zhí)行旋轉(zhuǎn)鎖,原子判斷是否可以加鎖,若可以,加鎖返回
3:當(dāng)前G休眠等待被喚醒:在執(zhí)行旋轉(zhuǎn)鎖期間,鎖還是沒釋放,那就只能讓當(dāng)前協(xié)程休眠,等待被喚醒,當(dāng)鎖被釋放后,當(dāng)前G被喚醒繼續(xù)執(zhí)行
釋放鎖(Mutex.UnLock)
1:將加鎖狀態(tài)去掉,判斷是否有等待的協(xié)程,如沒有直接返回
2:若有等待協(xié)程,將狀態(tài)設(shè)置成喚醒狀態(tài)
3:喚醒一個(gè)等待協(xié)程
三:讀寫鎖
讀寫鎖基于普通鎖實(shí)現(xiàn)
加寫鎖(RWMutex.Lock)
1:加普通鎖
2:改讀鎖的數(shù)量readerCount -= 1 << 30
3:如果有正在讀的鎖,等待直到讀鎖完成,讀寫不能同時(shí)進(jìn)行
釋放寫鎖(RWMutex.UnLock)
1:改讀鎖的數(shù)量readerCount += 1 << 30,加鎖的時(shí)候減了這么多,釋放鎖的時(shí)候加回來
2:如果readerCount>= 1 << 30,拋異常,釋放沒有加鎖的鎖
3:喚醒所有正在等待讀的協(xié)程
4:釋放普通鎖
加讀鎖(RWMutex.RLock)
1:原子操作讀鎖數(shù)量加1,readerCount+=1
2:如果rederCount<0,說明有寫功能正在執(zhí)行,協(xié)程進(jìn)入睡眠狀態(tài),等待寫完之后被喚醒
3:如果沒有正在執(zhí)行的寫鎖,就完事了,整個(gè)加鎖操作就只執(zhí)行了一個(gè)原子操作,還是很快的
釋放讀鎖(RWMutex.RUnLock)
1:原子操作讀鎖數(shù)量減1
2:如果讀鎖數(shù)量==-1,或==-1 << 30,說明釋放了一個(gè)沒有加讀鎖的鎖,或者釋放了一個(gè)正在寫的鎖,直接報(bào)錯(cuò)
3:如果有正在等待的寫鎖,喚醒它,否則整個(gè)釋放讀鎖也就執(zhí)行了一個(gè)原子操作
所以說,鎖是基于原子操作的,原子操作保證了數(shù)據(jù)的一致性,讀寫鎖基于普通鎖來實(shí)現(xiàn),對(duì)于一個(gè)寫少讀多的程序來說,讀寫鎖會(huì)比普通鎖快很多
加鎖原理
1:先是CAS的方式嘗試獲取鎖,如果獲取到了,就鎖住,并繼續(xù)執(zhí)行被鎖住的代碼,然后在釋放鎖
2:CAS沒有拿到鎖,就只能等待了,比如有10個(gè)協(xié)程(G)在等這個(gè)待鎖,go并不是一把鎖創(chuàng)建一個(gè)隊(duì)列,而是默認(rèn)創(chuàng)建251個(gè)隊(duì)列,通過hash的方式將G加入隊(duì)列,確保等待同一把鎖的G在同一個(gè)隊(duì)列,然后將當(dāng)前G執(zhí)行上下文信息保存到G.sched,下次就可以繼續(xù)從這里執(zhí)行,這樣這個(gè)等待的G就這樣被扔到隊(duì)列中了,而不是將這個(gè)G狀態(tài)改成等待狀態(tài)等待被喚醒,G去睡覺了,P還得繼續(xù)執(zhí)行,于是會(huì)找一個(gè)P,繼續(xù)執(zhí)行
解鎖原理
1:通過鎖定位到對(duì)應(yīng)的隊(duì)列,所有等待這把鎖的G都在這個(gè)隊(duì)列中,查找是否有等待的G,沒有就返回
2:有就將G狀態(tài)改成可運(yùn)行,并加入到運(yùn)行隊(duì)列,等待被調(diào)度
關(guān)于G調(diào)度請(qǐng)看我的這篇文章:go并發(fā)調(diào)度原理學(xué)習(xí)
浙公網(wǎng)安備 33010602011771號(hào)