盤點iOS開發(fā)中的線程鎖

線程鎖使用場景:在多個線程下操作同一個數(shù)據(jù)耕蝉,數(shù)據(jù)將變得不安全韩肝。比方說:在多個線程中刪除一個數(shù)組的首個元素焊切,你不知道在多線程操作過程中砰碴,該元素還存不存在躏筏,如果不存在程序就會崩潰。
加了線程鎖以后呈枉,就能保證在A線程訪問數(shù)據(jù)的時候趁尼,B線程就沒有辦法訪問。只有在A線程執(zhí)行完解鎖操作以后猖辫,B線程才有資格去訪問酥泞。也就是說該數(shù)據(jù)只允許被一個線程訪問,這就是線程安全啃憎。

針對這個問題芝囤,我們一起來盤點下iOS開發(fā)中的線程鎖。
一辛萍、 原子鎖: atomic

atomic@property創(chuàng)建屬性默認的關(guān)鍵字悯姊,使用atmoic關(guān)鍵字會在屬性的setter方法里面加上了,如下面代碼贩毕。因為手機設(shè)備資源有限悯许,為了提高效率在iOS開發(fā)中我們一般上使用nonatomic關(guān)鍵字。

  {lock}
  if (property != newValue) {
     [property release]; 
     property = [newValue retain]; 
  }    
  {unlock}

Objective-C Property Attributes這篇文章中詳細解釋了atomicnonatomic辉阶。劃重點:Atomic is really commonly confused with being thread-safe, and that is not correct. You need to guarantee your thread safety other ways.就是說用了atomic關(guān)鍵字并不能保證數(shù)據(jù)是線程安全的先壕,它只能保證你拿到的值是完好無損的。

- (void)multiOperation {
    for (int i=0; i<10; i++) {
        NSString *queue = [NSString stringWithFormat:@"queue-%d", i];
        dispatch_queue_t q = dispatch_queue_create([queue UTF8String], NULL);
        dispatch_async(q, ^{
            self.string = queue;
        });
    }
}
調(diào)用多次的結(jié)果:
string === queue-1 
string === queue-5 
string === queue-4 
string === (null) 
string === (null) 
string === queue-0 
string === queue-8 
string === queue-6 

上面這個例子里面string屬性是nonatomic修飾的谆甜,連續(xù)多次調(diào)用結(jié)果是隨機的垃僚,中間也會出現(xiàn)string === (null)的情況。如果用atomic來修飾結(jié)果也是隨機的但中間不會出現(xiàn)string === (null)规辱,這也就解釋了上面的結(jié)論:atomic關(guān)鍵字并不能保證數(shù)據(jù)是線程安全的谆棺,它只能保證你拿到的值是完好無損的。
為什么會出現(xiàn)string === (null)呢按摘?回到setter代碼塊包券,如果不加原子鎖該屬性在多線程賦值的過程中碰巧兩個線程接連執(zhí)行了release操作纫谅,當該屬性的retainCount=0的時候也就釋放了,所以就出現(xiàn)了null值溅固。

二付秕、NSLock & NSCondition & NSConditionLock & NSRecursiveLock

這四個是蘋果封裝好的線程鎖對象,統(tǒng)一定義在NSFoundation -> NSLock.h文件里面侍郭,都遵守了NSLocking協(xié)議询吴。

  @protocol NSLocking
  - (void)lock; 
  - (void)unlock;
  @end

所以,基本使用也很類似:

  1. 初始化一個鎖對象
  2. 執(zhí)行上鎖操作[xxx lock]
  3. 執(zhí)行解鎖操作[xxx unlock]

不同的是使用場景:
NSLock:最簡單的線程鎖亮元,沒有復(fù)雜的需求使用它就好了猛计。
NSCondition & NSConditionLock:條件鎖,滿足一定條件觸發(fā)的線程鎖爆捞。
NSRecursiveLock:遞歸鎖奉瘤,在遞歸調(diào)用使用線程鎖很容易造成死鎖,遞歸鎖就是為了解決這些問題設(shè)計的煮甥。具體使用參照:NSRecursiveLock遞歸鎖的使用

三盗温、synchronized關(guān)鍵字

為了避免多個線程同時執(zhí)行同一段代碼,Objective-C提供了@synchronized()成肘。它可以對一段代碼進行加鎖卖局,同一時間只允許一個線程執(zhí)行該代碼,其他試圖執(zhí)行該代碼的線程都會被阻塞双霍。和NSLock等線程鎖對比砚偶,@synchronized()使用起來更加方便,可讀性更高洒闸。
至于@synchronized()的底層實現(xiàn)染坯,可以看這篇文章關(guān)于 @synchronized,這兒比你想知道的還要多顷蟀。

四酒请、信號量 dispatch_semaphore_t

信號量(dispatch_semaphore):信號量是一個整形值并且具有一個初始計數(shù)值骡技,并且支持兩個操作:信號通知和等待鸣个。當一個信號量被信號通知,其計數(shù)會被增加布朦。當一個線程在一個信號量上等待時囤萤,線程會被阻塞(如果有必要的話),直至計數(shù)器大于零是趴,然后線程會減少這個計數(shù)涛舍。
在GCD中有三個函數(shù)是semaphore的操作,分別是:dispatch_semaphore_create:創(chuàng)建一個semaphore唆途,需要傳入一個long類型的數(shù)富雅,作為信號總量掸驱。
dispatch_semaphore_signal:發(fā)送一個信號,讓信號總量加1没佑。
dispatch_semaphore_wait:發(fā)送一個等待信號毕贼,讓信號總量減1

    dispatch_group_t group = dispatch_group_create();   
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);   
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   
    for (int i = 0; i < 100; i++)   
    {   
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);   
        dispatch_group_async(group, queue, ^{   
            NSLog(@"%i",i);   
            sleep(2);   
            dispatch_semaphore_signal(semaphore);   
        });   
    }   
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);   
    dispatch_release(group);   
    dispatch_release(semaphore);

這段代碼創(chuàng)建了一個初始值為10的信號量,每一次循環(huán)都會發(fā)送一個等待信號蛤奢,并創(chuàng)建一個線程鬼癣,該線程執(zhí)行完以后發(fā)送一個信號。當創(chuàng)建了10個線程以后啤贩,for循環(huán)就會阻塞待秃,等待有線程執(zhí)行完以后才會繼續(xù)執(zhí)行。這就形成了對并發(fā)的控制痹屹,上面是創(chuàng)建了一個并發(fā)數(shù)為10的線程隊列章郁。如果要做一個并發(fā)數(shù)為1的線程鎖,只需要創(chuàng)建一個初始值為1的信號量就可以了志衍。

五驱犹、補充 POSIX(pthread_mutex) & OSSpinLock

POSIX(pthread_mutex):Linux 線程鎖詳解
OSSpinLock:不再安全的 OSSpinLock
PS:POSIX(pthread_mutex)Linux底層的API,復(fù)雜的多線程處理建議使用足画,并且可以封裝自己的多線程雄驹;OSSpinLock已經(jīng)出現(xiàn)了BUG,導(dǎo)致并不能完全保證是線程安全的淹辞。


DEMO:
Objective-C-ThreadLock
參考文章:
iOS多線程 -各種線程鎖的簡單介紹
深入理解 iOS 開發(fā)中的鎖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末医舆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子象缀,更是在濱河造成了極大的恐慌蔬将,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件央星,死亡現(xiàn)場離奇詭異霞怀,居然都是意外死亡,警方通過查閱死者的電腦和手機莉给,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門毙石,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颓遏,你說我怎么就攤上這事徐矩。” “怎么了叁幢?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵滤灯,是天一觀的道長。 經(jīng)常有香客問我,道長鳞骤,這世上最難降的妖魔是什么窒百? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮豫尽,結(jié)果婚禮上贝咙,老公的妹妹穿的比我還像新娘。我一直安慰自己拂募,他們只是感情好庭猩,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著陈症,像睡著了一般蔼水。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上录肯,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天趴腋,我揣著相機與錄音,去河邊找鬼论咏。 笑死优炬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的厅贪。 我是一名探鬼主播蠢护,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼养涮!你這毒婦竟也來了葵硕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤贯吓,失蹤者是張志新(化名)和其女友劉穎懈凹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悄谐,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡介评,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了爬舰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片们陆。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖洼专,靈堂內(nèi)的尸體忽然破棺而出棒掠,到底是詐尸還是另有隱情孵构,我是刑警寧澤屁商,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響蜡镶,放射性物質(zhì)發(fā)生泄漏雾袱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一官还、第九天 我趴在偏房一處隱蔽的房頂上張望芹橡。 院中可真熱鬧,春花似錦望伦、人聲如沸林说。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腿箩。三九已至,卻和暖如春劣摇,著一層夾襖步出監(jiān)牢的瞬間珠移,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工末融, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钧惧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓勾习,卻偏偏與公主長得像浓瞪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巧婶,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

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