iOS中鎖的分析

iOS中鎖的分析

image.jpeg

** @synchronized ** 遞歸互斥鎖

// objc_sync_enter lock 加鎖
// objc_sync_exit 解鎖
recursive_mutex_t 遞歸鎖
1卖哎、當(dāng)如果鎖的是nil送膳,那么久直接retrun返回
2私植、SyncData 進(jìn)行了兩種方案,一種是從棧里面保存這個(gè)鎖,一種是用cache里面保存這把鎖
objc_sync_enter & objc_sync_exit 分析

  • 進(jìn)入objc_sync_enter源碼實(shí)現(xiàn)

  • 如果obj存在邑时,則通過(guò)id2data方法獲取相應(yīng)的SyncData,對(duì)threadCount、lockCount進(jìn)行遞增操作

  • 如果obj不存在客给,則調(diào)用objc_sync_nil,通過(guò)符號(hào)斷點(diǎn)得知肢簿,這個(gè)方法里面什么都沒(méi)做靶剑,直接return了

    image.jpeg

image.jpeg


data->mutex.lock();//加鎖

  • 進(jìn)入objc_sync_exit源碼實(shí)現(xiàn)

  • 如果obj存在,則調(diào)用id2data方法獲取對(duì)應(yīng)的SyncData池充,對(duì)threadCount桩引、lockCount進(jìn)行遞減操作

  • 如果obj為nil,什么也不做

  • image.jpeg

進(jìn)入SyncData的定義收夸,是一個(gè)結(jié)構(gòu)體坑匠,主要用來(lái)表示一個(gè)線程data,類似于鏈表結(jié)構(gòu)卧惜,有next指向厘灼,且封裝了recursive_mutex_t屬性,可以確認(rèn)@synchronized確實(shí)是一個(gè)遞歸互斥鎖

image.jpeg

【第一步】首先在tls即線程緩存中查找咽瓷。

  • 在tls_get_direct方法中以線程為key设凹,通過(guò)KVC的方式獲取與之綁定的SyncData,即線程data忱详。其中的tls()围来,表示本地局部的線程緩存,
  • 判斷獲取的data是否存在匈睁,以及判斷data中是否能找到對(duì)應(yīng)的object
  • 如果都找到了监透,在tls_get_direct方法中以KVC的方式獲取lockCount,用來(lái)記錄對(duì)象被鎖了幾次(即鎖的嵌套次數(shù))
  • 如果data中的threadCount 小于等于0航唆,或者 lockCount 小于等于0時(shí)胀蛮,則直接崩潰
  • 通過(guò)傳入的why,判斷是操作類型
  • 如果是ACQUIRE糯钙,表示加鎖粪狼,則進(jìn)行l(wèi)ockCount++,并保存到tls緩存
  • 如果是RELEASE任岸,表示釋放再榄,則進(jìn)行l(wèi)ockCount--,并保存到tls緩存享潜。如果lockCount 等于 0困鸥,從tls中移除線程data
  • 如果是CHECK,則什么也不做

** 【第二步】如果tls中沒(méi)有剑按,則在cache緩存中查找*

  • 通過(guò)fetch_cache方法查找cache緩存中是否有線程

  • 如果有疾就,則遍歷cache總表澜术,讀取出線程對(duì)應(yīng)的SyncCacheItem

  • 從SyncCacheItem中取出data,然后后續(xù)步驟與tls的匹配是一致的

    【第三步】如果cache中也沒(méi)有猬腰,即第一次進(jìn)來(lái)鸟废,則創(chuàng)建SyncData,并存儲(chǔ)到相應(yīng)緩存中

如果在cache中找到線程姑荷,且與object相等盒延,則進(jìn)行賦值、以及threadCount++
如果在cache中沒(méi)有找到厢拭,則threadCount等于1

所以在id2data方法中兰英,主要分為三種情況

【第一次進(jìn)來(lái),沒(méi)有鎖】:

  • threadCount = 1
  • lockCount = 1
  • 存儲(chǔ)到tls 【不是第一次進(jìn)來(lái)供鸠,且是同一個(gè)線程】
  • tls中有數(shù)據(jù)畦贸,則lockCount++
  • 存儲(chǔ)到tls 【不是第一次進(jìn)來(lái),且是不同線程】
  • 全局線程空間進(jìn)行查找線程
  • threadCount++
  • lockCount++
  • 存儲(chǔ)到cache 總結(jié)
  • @synchronized在底層封裝的是一把遞歸鎖楞捂,所以這個(gè)鎖是遞歸互斥鎖薄坏,底層通過(guò)TLS緩存和cache緩存
  • @synchronized的可重入,即可嵌套寨闹,主要是由于lockCount 和 threadCount的搭配
  • @synchronized使用鏈表的原因是鏈表方便下一個(gè)data的插入胶坠,
  • 但是由于底層中鏈表查詢、緩存的查找以及遞歸繁堡,是非常耗內(nèi)存以及性能的沈善,導(dǎo)致性能低,所以在前文中椭蹄,該鎖的排名在最后
  • 但是目前該鎖的使用頻率仍然很高闻牡,主要是因?yàn)榉奖愫?jiǎn)單,且不用解鎖
  • 不能使用非OC對(duì)象作為加鎖對(duì)象绳矩,因?yàn)槠鋙bject的參數(shù)為id
  • @synchronized (self)這種適用于嵌套次數(shù)較少的場(chǎng)景罩润。這里鎖住的對(duì)象也并不永遠(yuǎn)是self,這里需要讀者注意
  • 如果鎖嵌套次數(shù)較多翼馆,即鎖self過(guò)多割以,會(huì)導(dǎo)致底層的查找非常麻煩,因?yàn)槠涞讓邮擎湵磉M(jìn)行查找应媚,所以會(huì)相對(duì)比較麻煩严沥,所以此時(shí)可以使用NSLock、信號(hào)量等

NSLock

image.jpeg

NSLock是對(duì)下層pthread_mutex的封裝中姜,使用如下


image.jpeg

請(qǐng)問(wèn)下面block嵌套block的代碼中消玄,會(huì)有什么問(wèn)題?
會(huì)出現(xiàn)一直等待的情況,主要是因?yàn)榍短资褂玫倪f歸莱找,使用NSLock(簡(jiǎn)單的互斥鎖,如果沒(méi)有回來(lái)嗜桌,會(huì)一直睡覺(jué)等待)奥溺,即會(huì)存在一直加lock,等不到unlock 的堵塞情況

pthread_mutex就是互斥鎖本身骨宠,當(dāng)鎖被占用浮定,其他線程申請(qǐng)鎖時(shí),不會(huì)一直忙等待层亿,而是阻塞線程并睡眠

NSRecursiveLock

NSRecursiveLock在底層也是對(duì)pthread_mutex的封裝

對(duì)比NSLock 和 NSRecursiveLock桦卒,其底層實(shí)現(xiàn)幾乎一模一樣,區(qū)別在于init時(shí)匿又,NSRecursiveLock有一個(gè)標(biāo)識(shí)PTHREAD_MUTEX_RECURSIVE方灾,而NSLock是默認(rèn)的

image.jpeg

NSCondition

  • NSCondition 是一個(gè)條件鎖,在日常開(kāi)發(fā)中使用較少碌更,與信號(hào)量有點(diǎn)相似:線程1需要滿足條件1才會(huì)往下走裕偿,否則會(huì)堵塞等待,知道條件滿足痛单。經(jīng)典模型是生產(chǎn)消費(fèi)者模型

  • NSCondition的對(duì)象實(shí)際上作為一個(gè)鎖 和 一個(gè)線程檢查器

  • 鎖主要 為了當(dāng)檢測(cè)條件時(shí)保護(hù)數(shù)據(jù)源嘿棘,執(zhí)行條件引發(fā)的任務(wù)

  • 線程檢查器主要是根據(jù)條件決定是否繼續(xù)運(yùn)行線程,即線程是否被阻塞


    image.jpeg

    其底層也是對(duì)下層pthread_mutex的封裝

  • NSCondition是對(duì)mutex和cond的一種封裝(cond就是用于訪問(wèn)和操作特定類型數(shù)據(jù)的指針)

  • wait操作會(huì)阻塞線程旭绒,使其進(jìn)入休眠狀態(tài)鸟妙,直至超時(shí)

  • signal操作是喚醒一個(gè)正在休眠等待的線程

  • broadcast會(huì)喚醒所有正在等待的線程

NSConditionLock

NSConditionLock是條件鎖,一旦一個(gè)線程獲得鎖挥吵,其他線程一定等待

相比NSConditionLock而言重父,NSCondition使用比較麻煩,所以推薦使用NSConditionLock蔫劣,其使用如下

image.jpeg

NSConditionLock坪郭,其本質(zhì)就是NSCondition + Lock

線程 1 調(diào)用[NSConditionLock lockWhenCondition:],此時(shí)此刻因?yàn)椴粷M足當(dāng)前條件脉幢,所以會(huì)進(jìn)入 waiting 狀態(tài)歪沃,當(dāng)前進(jìn)入到 waiting 時(shí),會(huì)釋放當(dāng)前的互斥鎖嫌松。

此時(shí)當(dāng)前的線程 3 調(diào)用[NSConditionLock lock:]沪曙,本質(zhì)上是調(diào)用 [NSConditionLock lockBeforeDate:],這里不需要比對(duì)條件值萎羔,所以線程 3 會(huì)打印

接下來(lái)線程 2 執(zhí)行[NSConditionLock lockWhenCondition:]液走,因?yàn)闈M足條件值,所以線程2 會(huì)打印,打印完成后會(huì)調(diào)用[NSConditionLock unlockWithCondition:],這個(gè)時(shí)候?qū)alue 設(shè)置為 1缘眶,并發(fā)送 boradcast, 此時(shí)線程 1 接收到當(dāng)前的信號(hào)嘱根,喚醒執(zhí)行并打印。

自此當(dāng)前打印為 線程 3->線程 2 -> 線程 1

[NSConditionLock lockWhenCondition:];這里會(huì)根據(jù)傳入的 condition 值和 Value 值進(jìn)行對(duì)比巷懈,如果不相等该抒,這里就會(huì)阻塞,進(jìn)入線程池顶燕,否則的話就繼續(xù)代碼執(zhí)行[NSConditionLock unlockWithCondition:]: 這里會(huì)先更改當(dāng)前的 value 值凑保,然后進(jìn)行廣播,喚醒當(dāng)前的線程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涌攻,一起剝皮案震驚了整個(gè)濱河市欧引,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恳谎,老刑警劉巖芝此,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惠爽,居然都是意外死亡癌蓖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)婚肆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)租副,“玉大人,你說(shuō)我怎么就攤上這事较性∮蒙” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵赞咙,是天一觀的道長(zhǎng)责循。 經(jīng)常有香客問(wèn)我,道長(zhǎng)攀操,這世上最難降的妖魔是什么院仿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮速和,結(jié)果婚禮上歹垫,老公的妹妹穿的比我還像新娘。我一直安慰自己颠放,他們只是感情好排惨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著碰凶,像睡著了一般暮芭。 火紅的嫁衣襯著肌膚如雪鹿驼。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天辕宏,我揣著相機(jī)與錄音畜晰,去河邊找鬼。 笑死瑞筐,一個(gè)胖子當(dāng)著我的面吹牛舷蟀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播面哼,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼扫步!你這毒婦竟也來(lái)了魔策?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤河胎,失蹤者是張志新(化名)和其女友劉穎闯袒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體游岳,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡政敢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胚迫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喷户。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖访锻,靈堂內(nèi)的尸體忽然破棺而出褪尝,到底是詐尸還是另有隱情,我是刑警寧澤期犬,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布河哑,位于F島的核電站,受9級(jí)特大地震影響龟虎,放射性物質(zhì)發(fā)生泄漏璃谨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一鲤妥、第九天 我趴在偏房一處隱蔽的房頂上張望佳吞。 院中可真熱鬧,春花似錦旭斥、人聲如沸容达。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)花盐。三九已至羡滑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間算芯,已是汗流浹背柒昏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熙揍,地道東北人职祷。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像届囚,于是被迫代替她去往敵國(guó)和親有梆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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

  • iOS 底層原理 文章匯總[http://www.reibang.com/p/412b20d9a0f6] 本文主...
    Style_月月閱讀 4,376評(píng)論 9 16
  • 寫(xiě)在前面 多線程在日常開(kāi)發(fā)中能起到性能優(yōu)化的作用意系,但是一旦沒(méi)用好就會(huì)造成線程不安全泥耀,本文就來(lái)講講如何保證線程安全 ...
    M_慕宸閱讀 532評(píng)論 0 5
  • iOS之武功秘籍 文章匯總[http://www.reibang.com/p/07991e5b1c30] 寫(xiě)在前...
    長(zhǎng)茳閱讀 597評(píng)論 0 2
  • 前言 多線程開(kāi)發(fā)是性能優(yōu)化常用的技術(shù),在多線程開(kāi)發(fā)中蛔添,線程安全是繞不開(kāi)的一個(gè)話題痰催。線程安全的定義,在之前的文章中也...
    虎啦吧唧的猴閱讀 1,094評(píng)論 0 2
  • 歡迎閱讀iOS探索系列(按序閱讀食用效果更加)iOS探索 alloc流程iOS探索 內(nèi)存對(duì)齊&malloc源碼iO...
    呂子喬_eabd閱讀 1,129評(píng)論 0 2