淺談go語言中的讀寫鎖和互斥鎖

Hello倦春,各位小伙伴大家好,我是小棧君落剪,近期氣溫有所下降睁本,希望各位小伙伴記得防寒保暖,不要感冒了哦忠怖。

本期分享主題是關(guān)于go語言中的鎖的應(yīng)用場(chǎng)景呢堰,以及為各位小伙伴介紹實(shí)戰(zhàn)應(yīng)用中最為廣泛的讀寫鎖和互斥鎖。

互聯(lián)網(wǎng)生態(tài)的日益繁榮凡泣,人們的生活便利得到了極大的提高枉疼,通過網(wǎng)上操作我們基本上可以實(shí)現(xiàn)很多需求。

網(wǎng)站瘋狂訪問的背后應(yīng)對(duì)的是一波接一波的挑戰(zhàn)鞋拟。所以在應(yīng)對(duì)系統(tǒng)的穩(wěn)定和并發(fā)的時(shí)候骂维,程序中的“鎖”就孕育而生。

互斥鎖

在編程中贺纲,引入了對(duì)象互斥鎖的概念航闺,來保證共享數(shù)據(jù)操作的完整性。每個(gè)對(duì)象都對(duì)應(yīng)于一個(gè)可稱為" 互斥鎖" 的標(biāo)記猴誊,這個(gè)標(biāo)記用來保證在任一時(shí)刻潦刃,只能有一個(gè)線程訪問該對(duì)象。

也就是將共享資源變成獨(dú)占資源稠肘「GΓ互斥鎖的應(yīng)用場(chǎng)景通常是寫大于讀操作的,它不同于讀寫鎖的讀者隨意訪問项阴,而寫者只有一個(gè)滑黔。

它代表的資源就是一個(gè)笆包,不管是讀者還是寫者,只要誰擁有了它略荡,那么其他人就只有等待解鎖后庵佣,隱約在腦海中浮現(xiàn)出“寶刀屠龍,誰與針鋒”的話語汛兜。

其實(shí)我們可以很形象的理解一下互斥鎖巴粪,資源就好比是一個(gè)廁所,很多人都想上廁所粥谬,但是坑位只有一個(gè)肛根,那么誰獲取了互斥鎖,那么誰就有權(quán)利進(jìn)去漏策,其他人只有在門口排隊(duì)等待派哲。也就是我們通常所說的阻塞。

圖片1.png

在go語言的sync包中也是有對(duì)于互斥鎖的解釋掺喻,互斥鎖的結(jié)構(gòu)體很簡(jiǎn)單芭届,并且他的接口就只有一個(gè)加鎖和一個(gè)解鎖操作。

當(dāng)value為空時(shí)就是一個(gè)解鎖的互斥鎖感耙,也就是其他人都可以來使用褂乍。并且當(dāng)互斥鎖第一次使用的時(shí)候就不能再被復(fù)制。

圖片2.png

并且在代碼中也有很詳細(xì)的說明即硼,有興趣的小伙伴可以參考代碼源碼進(jìn)行了解逃片。大致的意思就是說互斥鎖有兩種模式,正常和饑餓模式谦絮。

在正常的模式下采用的是FIFO模式即先進(jìn)先出题诵,但是會(huì)被等待者喚醒。沒有擁有互斥鎖的等待者會(huì)同新來的協(xié)程進(jìn)行競(jìng)爭(zhēng)层皱,獲取鎖的使用權(quán)。

但是新來的協(xié)程擁有一個(gè)優(yōu)勢(shì)就是他們是運(yùn)行在CPU上的赠潦,而之前有可能會(huì)有很多進(jìn)程或協(xié)程需要被喚醒叫胖,所以他們有可能在毫秒之間就被人插隊(duì)了。

也就是新來的不需要被喚醒直接獲取到她奥。如果一個(gè)在1ms的時(shí)間內(nèi)沒有獲取到互斥量瓮增,那么它將進(jìn)入到饑餓模式。

也就是說在互斥鎖的饑餓模式下他會(huì)進(jìn)行有序的交接哩俭。也就是會(huì)將互斥鎖的所有權(quán)進(jìn)行移交到排在前面的等待者绷跑。

而新來的等待者想要獲取互斥鎖就只有乖乖排隊(duì)。他也不會(huì)試圖去搶占互斥鎖凡资。如果說在饑餓模式下他是最后一個(gè)互斥鎖的擁有者的話砸捏,或是等待少于1ms獲取鎖,那么他就會(huì)重新轉(zhuǎn)變?yōu)檎DJ健?/p>

其實(shí)在正常模式下協(xié)程會(huì)有更好的性能,但是饑餓模式是為了預(yù)防更多不確定的情況垦藏。

互斥鎖實(shí)戰(zhàn):

我們先進(jìn)行一個(gè)簡(jiǎn)單的模擬梆暖,定義個(gè)Map進(jìn)行模擬數(shù)據(jù)庫對(duì)象,制定一個(gè)Map的切片來做為數(shù)據(jù)對(duì)象的id和name掂骏,并且進(jìn)行數(shù)據(jù)的初始化轰驳,當(dāng)我們開啟10個(gè)并發(fā)請(qǐng)求進(jìn)行修改某一個(gè)值的時(shí)候。

圖片3.png

我們可以看到最終的結(jié)果是修改成功了弟灼。但是各位小伙伴我們是真的就沒有任何問題了么级解?

在go語言中我們其實(shí)可以檢查是否有問題可以使用 go build -race 來進(jìn)行數(shù)據(jù)競(jìng)爭(zhēng)檢測(cè)。

[小知識(shí):以后小伙伴在不確定的情況下都可以進(jìn)行使用命令進(jìn)行驗(yàn)證田绑,當(dāng)我們不確定打包工具有哪些命令的時(shí)候勤哗,我們可以用go help build 來進(jìn)行查看]

4.jpg

雙擊后我們可以看到出現(xiàn)了以下問題,看來當(dāng)初我們直接使用goland進(jìn)行運(yùn)行看到的表現(xiàn)往往有一絲絲的不妥當(dāng)辛馆,還是太年輕了鞍陈!

5.jpg

我們也可以看到在點(diǎn)擊文件后得到的結(jié)論是確實(shí)對(duì)于共享資源的搶占是會(huì)導(dǎo)致問題的昙篙,而且在go語言中我們也是很友好的進(jìn)行了提前的測(cè)試腊状,避免了在線上問題排查。

對(duì)于線程相關(guān)的問題在生產(chǎn)上其實(shí)排查相對(duì)而言是有點(diǎn)困難的苔可。在文件中也可以明確的顯示出問題出現(xiàn)在17行缴挖,也就是我們?cè)趯?duì)于Map切片進(jìn)行操作的時(shí)候。

6.jpg

所以我們針對(duì)多并發(fā)訪問的時(shí)候需要對(duì)共享資源進(jìn)行加鎖處理焚辅,目前模擬的應(yīng)用場(chǎng)景是寫大于讀的時(shí)候映屋。我們使用互斥鎖進(jìn)行相應(yīng)的操作。

得到的結(jié)論和之前的一樣同蜻,但是我們同樣需要對(duì)這段程序進(jìn)行g(shù)o build -race 操作棚点。

7.jpg

最終得到了正確無誤的操作。以上就是我們關(guān)于互斥鎖的初步使用湾蔓。按照使用規(guī)范來講瘫析,不管是互斥鎖和讀寫鎖,我們都應(yīng)該盡可能的對(duì)于小范圍的進(jìn)行使用默责,在關(guān)鍵處進(jìn)行使用贬循,避免程序擁有大量的阻塞。

讀寫鎖

讀寫鎖實(shí)際是一種特殊的自旋鎖桃序,它把對(duì)共享資源的訪問者劃分成讀者和寫者杖虾,讀者只對(duì)共享資源進(jìn)行讀訪問,寫者則需要對(duì)共享資源進(jìn)行寫操作媒熊。

這種鎖相對(duì)于自旋鎖而言奇适,能提高并發(fā)性坟比,因?yàn)樵诙嗵幚砥飨到y(tǒng)中,它允許同時(shí)有多個(gè)讀者來訪問共享資源滤愕,最大可能的讀者數(shù)為實(shí)際的邏輯CPU數(shù)温算。

寫者是排他性的,一個(gè)讀寫鎖同時(shí)只能有一個(gè)寫者或多個(gè)讀者(與CPU數(shù)相關(guān))间影,但不能同時(shí)既有讀者又有寫者注竿。在讀寫鎖保持期間也是搶占失效的。

如果讀寫鎖當(dāng)前沒有讀者魂贬,也沒有寫者巩割,那么寫者可以立刻獲得讀寫鎖,否則它必須自旋在那里付燥,直到?jīng)]有任何寫者或讀者宣谈。

如果讀寫鎖沒有寫者,那么讀者可以立即獲得該讀寫鎖键科,否則讀者必須自旋在那里闻丑,直到寫者釋放該讀寫鎖。

所以針對(duì)于系統(tǒng)中需要讀多寫少的情況下勋颖,我們就需要使用到讀寫鎖進(jìn)行應(yīng)對(duì)程序的并發(fā)嗦嗡,保護(hù)程序的安全、穩(wěn)定饭玲、高效的運(yùn)行侥祭。

在go語言中內(nèi)置包中已經(jīng)實(shí)現(xiàn)了關(guān)于讀寫鎖操作,我們?cè)谙到y(tǒng)中需要使用可以很方便的進(jìn)行操作茄厘。

8.jpg

在代碼注釋中很明確的可以看出矮冬,在go語言中的sync包中的RWMutex就是一個(gè)讀寫互斥鎖,該鎖可以有任意數(shù)量的讀者或是只有一個(gè)寫者持有次哈,形象的說就好比一場(chǎng)電影可以有無數(shù)的人來看胎署,但是導(dǎo)演就只有一個(gè)。

當(dāng)值為空的時(shí)候窑滞,他是處于解鎖狀態(tài)硝拧,并且在讀寫鎖第一次使用的時(shí)候是不允許被拷貝的。如果一個(gè)協(xié)程(goroutine)持有讀的權(quán)限葛假,另一個(gè)協(xié)程進(jìn)行鎖操作,那么沒有任何一個(gè)協(xié)程能夠再持有讀鎖滋恬,除非被釋放聊训。

當(dāng)然開發(fā)者也提醒了禁止進(jìn)行遞歸讀鎖定,是為了保證鎖能夠一直可用恢氯。大概意思就是說這部電影不能讓你一個(gè)人獨(dú)自看带斑,畢竟獨(dú)樂樂不如眾樂樂嘛鼓寺。

在代碼中我們常用的兩個(gè)操作就是Lock 和Unlock 即加解鎖操作,通過代碼中我們可以知道go語言最多讀者可以高達(dá)10億個(gè)勋磕,已經(jīng)能夠完美的滿足到我們的業(yè)務(wù)需求了妈候。

9.jpg

讀寫鎖實(shí)戰(zhàn):

接下來我們將模擬一下關(guān)于網(wǎng)頁中的讀寫鎖操作,讀寫鎖的應(yīng)用場(chǎng)景多數(shù)是類似于朋友圈或微博的并發(fā)場(chǎng)景下的讀大于寫的場(chǎng)景挂滓,所以我們用讀寫兩個(gè)循環(huán)協(xié)程進(jìn)行模擬數(shù)據(jù)庫的請(qǐng)求操作苦银,并是使用count進(jìn)行原子計(jì)數(shù),最終得到的結(jié)果如下:

10.jpg

當(dāng)然如果我們依舊關(guān)閉鎖操作會(huì)得到相同的結(jié)果么赶站?

11.jpg

使用goland執(zhí)行后的結(jié)果和之前加鎖的狀態(tài)是是沒有什么區(qū)別幔虏,但是真的沒有什么區(qū)別么?

讓我們使用一下go build -race 命令贝椿,最后得到的結(jié)果如下:

12.jpg

表面風(fēng)輕云淡的情況下內(nèi)部已經(jīng)翻江倒海了想括,所以小棧君在這里也是奉勸各位三思而后行。

讀寫鎖與互斥鎖性能大比拼

讀寫鎖和互斥鎖的各自實(shí)戰(zhàn)情況已經(jīng)初略的給各位分享了一遍烙博,總體而言用法是比較簡(jiǎn)單的瑟蜈,并且有興趣的小伙伴可以看看go語言的內(nèi)置包,后續(xù)我也會(huì)陸續(xù)為大家分享關(guān)于go語言實(shí)現(xiàn)二叉樹渣窜,鏈表結(jié)構(gòu)等文章铺根,讓大家更加深入的感受到go語言的魅力。

當(dāng)然我也在籌劃其他語言的分享图毕,所以各位小伙伴夷都,如果你喜歡我的文章,麻煩分享并關(guān)注小棧君哦予颤。

回歸正題囤官,我們?cè)谑褂酶髯缘氖褂脠?chǎng)景下并沒有感受到讀寫鎖魚互斥鎖性能上有多大的區(qū)別。所以小棧君接下來就一個(gè)場(chǎng)景分別使用兩個(gè)鎖來進(jìn)行模擬請(qǐng)求計(jì)數(shù)蛤虐,得出結(jié)論党饮。

我們首先使用讀寫鎖進(jìn)行模擬如圖所示:

13.jpg

我們先定義讀寫鎖、數(shù)據(jù)庫數(shù)據(jù)驳庭、還有相關(guān)的原子計(jì)數(shù)器刑顺。這里有一個(gè)小知識(shí)點(diǎn),我們?cè)谟?jì)數(shù)的時(shí)候用了atomic饲常,這樣可以保證計(jì)數(shù)的準(zhǔn)確和安全蹲堂。然后開啟了一個(gè)寫協(xié)程和一百個(gè)讀協(xié)程。

為了加大請(qǐng)求力度我們有在每個(gè)協(xié)程里開啟了無線循環(huán)讀取贝淤,用1毫秒模擬查詢時(shí)間柒竞,用10毫秒模擬寫時(shí)間。最終得出的結(jié)論是計(jì)數(shù)器計(jì)算到了16萬接近17萬次播聪。

14.jpg

然后相同的代碼我們僅僅只修改了一下鎖的機(jī)制朽基,將讀寫鎖改成了互斥鎖布隔,各位可以看到效果就是代碼執(zhí)行了1695次,相差是非常大的稼虎。

15.jpg
16.jpg

以上就是關(guān)于go語言中的互斥鎖和讀寫鎖的分享衅檀。事實(shí)證明,只有鎖用的對(duì)霎俩,我們就可以早些下班啦哀军。

好了,今天的分享就到這啦茸苇,如果你喜歡我的分享排苍,麻煩你點(diǎn)擊一個(gè)好看或贊,我是小棧君学密,不定期分享IT干貨淘衙,包括但不限于區(qū)塊鏈、大數(shù)據(jù)腻暮、Python彤守、go、等系列專題哭靖。原創(chuàng)不易具垫,更新較慢,多多包涵试幽。希望與你共同成長(zhǎng)筝蚕。我們下期再見啦,拜了個(gè)拜~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铺坞,一起剝皮案震驚了整個(gè)濱河市起宽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌济榨,老刑警劉巖坯沪,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異擒滑,居然都是意外死亡腐晾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門丐一,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藻糖,“玉大人,你說我怎么就攤上這事库车∮庇” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)潘拱。 經(jīng)常有香客問我,道長(zhǎng)拧略,這世上最難降的妖魔是什么芦岂? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮垫蛆,結(jié)果婚禮上禽最,老公的妹妹穿的比我還像新娘。我一直安慰自己袱饭,他們只是感情好川无,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著虑乖,像睡著了一般懦趋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疹味,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天仅叫,我揣著相機(jī)與錄音,去河邊找鬼糙捺。 笑死诫咱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洪灯。 我是一名探鬼主播坎缭,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼签钩!你這毒婦竟也來了掏呼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤边臼,失蹤者是張志新(化名)和其女友劉穎哄尔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柠并,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岭接,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了臼予。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸣戴。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖粘拾,靈堂內(nèi)的尸體忽然破棺而出窄锅,到底是詐尸還是另有隱情,我是刑警寧澤缰雇,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布入偷,位于F島的核電站追驴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏疏之。R本人自食惡果不足惜殿雪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锋爪。 院中可真熱鬧丙曙,春花似錦、人聲如沸其骄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虽抄。三九已至馆揉,卻和暖如春龄砰,著一層夾襖步出監(jiān)牢的瞬間眷蜓,已是汗流浹背员魏。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工誓沸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诸迟,地道東北人否副。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓汉矿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親备禀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子洲拇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容