iOS:多線程與鎖

按照功能來(lái)區(qū)分鎖:

互斥鎖(mutexlock)sleep-waiting:

保證共享數(shù)據(jù)操作的完整性, 鎖被占用的時(shí)候會(huì)休眠, 等待鎖釋放的時(shí)候會(huì)喚醒经窖。
在訪問(wèn)共享資源之前進(jìn)行加鎖主守,訪問(wèn)完成后解鎖框杜。
線程A獲取到鎖,在釋放鎖之前塑陵,其他線程都獲取不到鎖傻工。
解鎖時(shí)鲜戒,如果有1個(gè)以上的線程阻塞师幕,那么所有該鎖上的線程變?yōu)榫途w狀態(tài)粟按,第一個(gè)就緒的加鎖,其他的又進(jìn)入休眠们衙。
從而實(shí)現(xiàn)在任意時(shí)刻钾怔,最多只有1個(gè)線程能夠訪問(wèn)被互斥鎖保護(hù)的資源。

自旋鎖(spinlock)busy-waiting:

跟互斥鎖類似, 只是資源被占用的時(shí)候, 會(huì)一直循環(huán)檢測(cè)鎖是否被釋放(CPU不能做其他的事情)
線程A獲取到鎖蒙挑,在釋放鎖之前,線程B 來(lái)獲取鎖愚臀,此時(shí)獲取不到忆蚀,線程B就會(huì)不斷的進(jìn)入循環(huán),一直檢查鎖是否已被釋放姑裂,如果釋放馋袜,則能獲取到鎖。
由于調(diào)用方會(huì)一直循環(huán)看該自旋鎖的的保持者是否已經(jīng)釋放了資源舶斧,節(jié)省了喚醒睡眠線程的內(nèi)核消耗欣鳖。
但是如果不能短時(shí)間獲得鎖,就會(huì)一直占用著CPU茴厉,造成效率低下泽台。
(在加鎖時(shí)間短暫的情況下會(huì)大大提高效率)

其他鎖都是這兩種鎖的延伸和擴(kuò)展。

常見(jiàn)的鎖

1矾缓、OSSpinLock

自旋鎖怀酷,會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)的問(wèn)題。當(dāng)一個(gè)低優(yōu)先級(jí)線程獲得鎖的時(shí)候嗜闻,如果此時(shí)一個(gè)高優(yōu)先級(jí)的系統(tǒng)到來(lái)蜕依,那么會(huì)進(jìn)入忙等狀態(tài),不會(huì)進(jìn)入睡眠琉雳,此時(shí)會(huì)一直占用著系統(tǒng)CPU時(shí)間样眠,導(dǎo)致低優(yōu)先級(jí)的無(wú)法拿到CPU時(shí)間片,從而無(wú)法完成任務(wù)也無(wú)法釋放鎖翠肘。除非能保證訪問(wèn)鎖的線程全部處于同一優(yōu)先級(jí)檐束,否則系統(tǒng)所有的自旋鎖都會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)的問(wèn)題。

現(xiàn)在蘋(píng)果的OSSpinLockiOS10+已廢棄锯茄,已經(jīng)被替換成os_unfair_lock厢塘。茶没,有興趣可以看看:不再安全的OSSpinLock

2晚碾、os_unfair_lock

互斥鎖抓半,iOS10+才支持,為了替代OSSpinLock格嘁。

os_unfair_lock 是一種適用于需要簡(jiǎn)單和輕量級(jí)互斥的情況下的鎖笛求。

此鎖必須從與鎖定它的相同線程中解鎖,嘗試從不同線程解鎖將導(dǎo)致斷言中止進(jìn)程糕簿。
必須使用 OS_UNFAIR_LOCK_INIT 進(jìn)行初始化探入。

使用時(shí)需要引入#import <os/lock.h>

// 必須在線程里初始化 且必須使用OS_UNFAIR_LOCK_INIT初始化
os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT); 
os_unfair_lock_lock(unfairLock); // 加鎖
......其他操作......
os_unfair_lock_unlock(unfairLock); // 解鎖

3、pthread_mutex

互斥鎖懂诗,等待鎖的線程會(huì)處于休眠狀態(tài)蜂嗽。

使用時(shí)需要引入頭文件#import <pthread.h>

  // 定義互斥鎖和互斥鎖屬性對(duì)象
  // 若不特殊設(shè)置互斥鎖屬性對(duì)象可以不設(shè)置
  // 初始化互斥鎖時(shí)的第二個(gè)參數(shù)可以傳NULL
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutex_attr;

    // 初始化互斥鎖屬性對(duì)象
    pthread_mutexattr_init(&mutex_attr);
    
    // 設(shè)置互斥鎖屬性為默認(rèn)類型
    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_DEFAULT);
    
    // 初始化互斥鎖
    pthread_mutex_init(&mutex, &mutex_attr);

    // 加鎖
    pthread_mutex_lock(&mutex);
    
    // 訪問(wèn)共享資源
    // 這里省略了共享資源的操作
    
    // 解鎖
    pthread_mutex_unlock(&mutex);
    
    // 銷毀互斥鎖
    pthread_mutex_destroy(&mutex);
    
    // 銷毀互斥鎖屬性對(duì)象
    pthread_mutexattr_destroy(&mutex_attr);
#define PTHREAD_MUTEX_NORMAL        0
#define PTHREAD_MUTEX_ERRORCHECK    1
#define PTHREAD_MUTEX_RECURSIVE        2
#define PTHREAD_MUTEX_DEFAULT        PTHREAD_MUTEX_NORMAL

互斥鎖類型具體為:
PTHREAD_MUTEX_NORMAL:普通鎖。默認(rèn)類型殃恒。不提供死鎖檢測(cè)或遞歸保護(hù)植旧。如果同一線程嘗試再次獲取鎖,可能會(huì)導(dǎo)致死鎖离唐。
PTHREAD_MUTEX_ERRORCHECK:錯(cuò)誤檢查鎖病附。提供死鎖檢測(cè)。如果同一線程嘗試再次獲取鎖亥鬓,函數(shù)將返回錯(cuò)誤 EBUSY完沪。
PTHREAD_MUTEX_RECURSIVE:遞歸鎖。允許同一線程多次獲取鎖而不會(huì)死鎖嵌戈。必須在解鎖時(shí)與加鎖次數(shù)匹配覆积。
PTHREAD_MUTEX_DEFAULT:等同于 PTHREAD_MUTEX_NORMAL。

4咕别、NSLock

互斥鎖技健,遵循NSLocking協(xié)議。內(nèi)部是封裝了pthread_mutext惰拱,類型是PTHREAD_MUTEXT_ERRORCHECK雌贱。

NSLock、NSCondition偿短、NSConditionLock欣孤、NSRecursiveLock都實(shí)現(xiàn)了NSLocking協(xié)議。了解更多查看官方文檔

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

-lock和-unlock必須在相同的線程調(diào)用昔逗。

5降传、NSCondition

互斥鎖,也遵循NSLocking協(xié)議勾怒。內(nèi)部是封裝了pthread_mutext婆排,和它和NSLock的區(qū)別是:NSLock在獲取不到鎖的時(shí)候自動(dòng)使線程進(jìn)入休眠声旺,鎖被釋放后線程又自動(dòng)被喚醒,NSCondition可以使我們更加靈活的控制線程狀態(tài)段只,在任何需要的時(shí)候使線程進(jìn)入休眠或喚醒它腮猖。

// 讓當(dāng)前線程處于等待狀態(tài)
- (void)wait;
// 向等待在條件上的一個(gè)線程發(fā)出信號(hào),可以繼續(xù)執(zhí)行
- (void)signal;
//   向等待在條件上的所有線程發(fā)出信號(hào)赞枕,從而喚醒所有線程
- (void)broadcast;
NSCondition *cLock = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [cLock lock];
    NSLog(@"線程1加鎖成功");
    [cLock wait];
    NSLog(@"線程1");
    [cLock unlock];

});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [cLock lock];
    NSLog(@"線程2加鎖成功");
    [cLock wait];
    sleep(1);
    NSLog(@"線程2");
    [cLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(2);
    NSLog(@"喚醒一個(gè)等待的線程");
    [cLock signal];
});

輸出:
線程1加鎖成功
線程2加鎖成功
喚醒一個(gè)等待的線程
線程1

6澈缺、NSConditionLock

互斥鎖,有條件的互斥鎖炕婶。

// 只讀屬性condition姐赡,保存鎖當(dāng)前的條件
@property (readonly) NSInteger condition;

// 解鎖并設(shè)置新的條件值。

-(void)unlockWithCondition:(NSInteger)condition;

// 條件值滿足時(shí)加鎖, 執(zhí)行之后代碼

-(BOOL)lockWhenCondition:(NSInteger)condition;

// 當(dāng)條件值等于指定值時(shí)嘗試加鎖柠掂。

-(BOOL)tryLockWhenCondition:(NSInteger)condition;

NSConditionLock *cLock = [[NSConditionLock alloc] initWithCondition:0]; // 設(shè)置初始標(biāo)志位
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  if ([cLock tryLockWhenCondition:0]) {
      NSLog(@"Task - 1, cLock.condition = %ld",cLock.condition);
      [cLock unlockWithCondition:1];
      NSLog(@"Task - 1, end - cLock.condition = %ld",cLock.condition);

  } else {
      NSLog(@"Task -  加鎖失敗");
  }
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [cLock lockWhenCondition:3];
    NSLog(@"Task - 2, cLock.condition = %ld",cLock.condition);
    [cLock unlockWithCondition:2];
    NSLog(@"Task - 2, end- cLock.condition = %ld",cLock.condition);
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [cLock lockWhenCondition:1];
    NSLog(@"Task - 3, cLock.condition = %ld",cLock.condition);
    [cLock unlockWithCondition:3];
    NSLog(@"Task - 3, end- cLock.condition = %ld",cLock.condition);

});

輸出:
Task - 1, cLock.condition = 0
Task - 1, end - cLock.condition = 1
Task - 3, cLock.condition = 1
Task - 3, end- cLock.condition = 3
Task - 2, cLock.condition = 3
Task - 2, end- cLock.condition = 2

7项滑、NSRecursiveLock

互斥鎖中的遞歸鎖。內(nèi)部使用的pthread_mutex_t的類型是PTHREAD_MUTEXT_RECURSIVE陪踩≌让牵可以被同一線程多次請(qǐng)求,而不會(huì)引起死鎖肩狂。這主要是用在循環(huán)或遞歸操作中。

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
NSLock *lo = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    static void (^RecursiveMethod)(int);
    RecursiveMethod = ^(int value) {
        NSLog(@"加鎖: %d", value);
        [lock lock]; // 加鎖
        if (value < 3) {
            sleep(2);
            RecursiveMethod(++value); // 解鎖之前又鎖上
        }
        NSLog(@"解鎖: %d", value);
        [lock unlock]; // 解鎖
    };
    RecursiveMethod(1);
    NSLog(@"finish");
});

輸出:
加鎖: 1
加鎖: 2
加鎖: 3
解鎖: 3
解鎖: 3
解鎖: 2
finish

8姥饰、@synchronized

@synchronized(id)的使用應(yīng)該是較多的傻谁,它底層實(shí)現(xiàn)是個(gè)遞歸鎖,不會(huì)產(chǎn)生死鎖列粪,且不需要手動(dòng)去加鎖解鎖审磁,使用起來(lái)比較方便。

@synchronized(self) {  
    //數(shù)據(jù)操作  
}

9岂座、atomic

修飾屬性關(guān)鍵字态蒂,對(duì)被修飾的進(jìn)行原子操作(不負(fù)責(zé)使用,只是setter/getter方法安全费什,其他不保證)點(diǎn)擊屬性關(guān)鍵字-atomic了解更多钾恢。

10、dispatch_semaphore_t

是GCD用來(lái)同步的一種方式鸳址。

GCD 信號(hào)量dispatch_semaphore可以用來(lái)控制最大并發(fā)數(shù)量瘩蚪,可以用來(lái)實(shí)現(xiàn) iOS 的線程同步方案。
信號(hào)量的初始值稿黍,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量疹瘦;
信號(hào)量的初始值為1,代表同時(shí)只允許 1 條線程訪問(wèn)資源巡球,保證線程同步言沐。
若設(shè)置信號(hào)量初始值為3.則控制最大并發(fā)數(shù)為3邓嘹。

// 創(chuàng)建一個(gè) Semaphore 并初始化信號(hào)的總量
dispatch_semaphore_create

// 發(fā)送一個(gè)信號(hào),讓信號(hào)總量加 1
dispatch_semaphore_signal

// 可以使總信號(hào)量減 1险胰,信號(hào)總量小于 0 時(shí)就會(huì)一直等待(阻塞所在線程)汹押,否則就可以正常執(zhí)行。
dispatch_semaphore_wait

點(diǎn)擊iOS:多線程鸯乃,了解更多信號(hào)量相關(guān)信息鲸阻。

鎖的關(guān)系及性能

鎖之間的關(guān)系

性能排序(從高到低):

1、os_unfair_lock

2缨睡、OSSpinLock(已經(jīng)不推薦使用)

3鸟悴、dispatch_semaphore

4、pthread_mutext

5奖年、NSLock

6细诸、NSCondition

7、NSRecursiveLock

8陋守、NSConditonLock

9震贵、@synchronized

死鎖

指多個(gè)進(jìn)程因競(jìng)爭(zhēng)共享資源而造成的一種僵局,若無(wú)外力作用水评,這些進(jìn)程都將永遠(yuǎn)不能再向前推進(jìn)猩系。

死鎖的條件

1、互斥:某種資源一次只允許一個(gè)進(jìn)程訪問(wèn)中燥,即該資源一旦分配給某個(gè)進(jìn)程寇甸,其他進(jìn)程就不能再訪問(wèn),直到該進(jìn)程訪問(wèn)結(jié)束疗涉。

2拿霉、占有且等待:一個(gè)進(jìn)程本身占有資源(一種或多種),同時(shí)還有資源未得到滿足咱扣,正在等待其他進(jìn)程釋放該資源绽淘。

3、不可搶占:別人已經(jīng)占有了某項(xiàng)資源闹伪,你不能因?yàn)樽约阂残枰撡Y源沪铭,就去把別人的資源搶過(guò)來(lái),只能在使用完時(shí)由自己釋放祭往。

4伦意、循環(huán)等待:存在一個(gè)進(jìn)程鏈,使得每個(gè)進(jìn)程都占有下一個(gè)進(jìn)程所需的至少一種資源硼补。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驮肉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子已骇,更是在濱河造成了極大的恐慌离钝,老刑警劉巖票编,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異卵渴,居然都是意外死亡慧域,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)浪读,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)昔榴,“玉大人,你說(shuō)我怎么就攤上這事碘橘』ザ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵痘拆,是天一觀的道長(zhǎng)仰禽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)纺蛆,這世上最難降的妖魔是什么吐葵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮桥氏,結(jié)果婚禮上温峭,老公的妹妹穿的比我還像新娘。我一直安慰自己字支,他們只是感情好诚镰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著祥款,像睡著了一般。 火紅的嫁衣襯著肌膚如雪月杉。 梳的紋絲不亂的頭發(fā)上刃跛,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音苛萎,去河邊找鬼桨昙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛腌歉,可吹牛的內(nèi)容都是我干的蛙酪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼翘盖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼桂塞!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起馍驯,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤阁危,失蹤者是張志新(化名)和其女友劉穎玛痊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體狂打,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡擂煞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了趴乡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片对省。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晾捏,靈堂內(nèi)的尸體忽然破棺而出丁逝,到底是詐尸還是另有隱情官份,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站酒甸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏忍宋。R本人自食惡果不足惜咧党,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望市怎。 院中可真熱鬧岁忘,春花似錦、人聲如沸区匠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)驰弄。三九已至麻汰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間戚篙,已是汗流浹背五鲫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岔擂,地道東北人位喂。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像乱灵,于是被迫代替她去往敵國(guó)和親塑崖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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

  • 前言 iOS開(kāi)發(fā)中由于各種第三方庫(kù)的高度封裝,對(duì)鎖的使用很少,剛好之前面試中被問(wèn)到的關(guān)于并發(fā)編程鎖的問(wèn)題聋呢,都是一知...
    iOS__開(kāi)發(fā)者皮皮峰閱讀 551評(píng)論 1 5
  • 前言 iOS開(kāi)發(fā)中由于各種第三方庫(kù)的高度封裝苗踪,對(duì)鎖的使用很少,剛好之前面試中被問(wèn)到的關(guān)于并發(fā)編程鎖的問(wèn)題削锰,都是一知...
    KingWorld閱讀 1,067評(píng)論 0 0
  • 多線程需要一種互斥的機(jī)制來(lái)訪問(wèn)共享資源通铲。 一、 互斥鎖 互斥鎖的意思是某一時(shí)刻只允許一個(gè)線程訪問(wèn)某一資源器贩。為了保證...
    doudo閱讀 714評(píng)論 0 5
  • 前言 iOS開(kāi)發(fā)中由于各種第三方庫(kù)的高度封裝颅夺,對(duì)鎖的使用很少,剛好之前面試中被問(wèn)到的關(guān)于并發(fā)編程鎖的問(wèn)題蛹稍,都是一知...
    喵渣渣閱讀 3,690評(píng)論 0 33
  • 1.概述 1.1 類型 自旋鎖 OSSpinLock 信號(hào)量 dispatch_semaphore 互斥鎖 ...
    George_Luofz閱讀 357評(píng)論 0 0