iOS多線程(四)

多線程安全隱患解決方案

1.解決方案:使用線程同步技術(shù)(協(xié)同步調(diào)誊抛,按預(yù)定的先后次序進(jìn)行)

2.常用的線程同步技術(shù):加鎖

3.iOS中的線程同步方案

OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

3.1OSSpinLock

3.1.1OSSpinLock叫做自旋鎖,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài),一直占用CPU資源
3.1.2 目前已經(jīng)不安全挥吵,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題
3.1.3 如果等待鎖的線程優(yōu)先級(jí)較高,它會(huì)一直占用著CPU資源花椭,優(yōu)先級(jí)低的線程就無(wú)法 釋放鎖
3.1.4 頭文件 #import <libkern/OSAtomic.h>
3.1.5 示例代碼如下
- (void)p_saleTicket {
//初始化
    static OSSpinLock ossPinLock = OS_SPINLOCK_INIT;
// 嘗試加鎖(如果需要等待就不加鎖,直接返回false房午,如果不需要等待就加鎖返回true)
//bool result  = OSSPinLockTry(&ossPinLock);
//加鎖
    OSSpinLockLock(&ossPinLock);
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
//解鎖
    OSSpinLockUnlock(&ossPinLock);
}

3.2 os_unfair_lock

3.2.1 os_unfair_lock用于取代不安全的OSSPinLock矿辽,iOS10開(kāi)始支持
3.2.2從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài)郭厌,并非忙等
3.2.3頭文件#import<os/lock.h>
3.2.4示例代碼如下
- (void)p_saleTicket3 {
    //初始化
    static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
    //嘗試加鎖
    //os_unfair_lock_trylock(&lock);
    //加鎖
    os_unfair_lock_lock(&lock);
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
    //解鎖
    os_unfair_lock_unlock(&lock);
}

3.3 pthread_mutex_t

3.2.1 mutex叫做"互斥鎖 ",等待鎖的線程會(huì)處于休眠狀態(tài)
3.2.2頭文件#import <pthread/pthread.h>
3.2.3示例代碼如下
// 靜態(tài)初始化
  //        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  
//    // 初始化屬性
//    pthread_mutexattr_t attr;
//    pthread_mutexattr_init(&attr);
//    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
//    // 初始化鎖
//    pthread_mutex_init(mutex, &attr);
//    // 銷(xiāo)毀屬性
//    pthread_mutexattr_destroy(&attr);
  
  // 初始化屬性
//    pthread_mutexattr_t attr;
//    pthread_mutexattr_init(&attr);
//    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
  // 初始化鎖
  pthread_mutex_init(mutex, NULL);
  // 銷(xiāo)毀屬性
//    pthread_mutexattr_destroy(&attr);

3.4 NSLock | NSRecursiveLock

  3.4.1.NSLock是對(duì)mutex普通鎖的封裝
  3.4.2.NSRecursiveLock也是對(duì)mutex遞歸鎖的封裝
 3.4.3示例代碼如下
- (void)p_saleTicket {
   [self.lock lock];
   sleep(1);
   int oldCount = self.ticketsCount;
   oldCount--;
   self.ticketsCount = oldCount;
   NSLog(@"余票%d",oldCount);
   [self.lock unlock];
}

3.5 NSCondition

3.5.1NSCondition是對(duì)mutex和cond的封裝
3.5.2常見(jiàn)API
- (void)wait; 讓當(dāng)前線程處于等待狀態(tài)
- (BOOL)waitUntilDate:(NSDate *)limit;讓當(dāng)前線程在一定時(shí)間內(nèi)處于等待狀態(tài)
- (void)signal;CPU發(fā)信號(hào)告訴當(dāng)前線程不用在等待袋倔,可以繼續(xù)執(zhí)行
- (void)broadcast;CPU發(fā)信號(hào)告訴所有線程不用在等待,可以繼續(xù)執(zhí)行
3.5.3示例代碼如下
- (void)p_saleTicket {

    [self.lock lock];
    if (self.ticketsCount == 12) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 信號(hào)
            [self.lock signal];
        });
        // 等待
        [self.lock wait];
    }
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
    [self.lock unlock];

}

3.6 NSConditionLock

3.6.1示例代碼如下
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one
{
    [self.conditionLock lock];
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two
{
    [self.conditionLock lockWhenCondition:2];
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three
{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

3.7 DISPATCH_QUEUE_SERIAL

3.7.4示例代碼如下 放到串行隊(duì)列里面

3.8 dispatch_semaphore_t

3.8.1semaphore叫做信號(hào)量
3.8.2信號(hào)量的初始值折柠,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量 不可以未負(fù)數(shù)
3.8.3信號(hào)量的初始值為1 宾娜,代表同時(shí)只允許1條線程訪問(wèn)資源,保證線程同步
3.8.4示例代碼如下
//    初始化信號(hào)量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//    如果信號(hào)量=0當(dāng)前線程就會(huì)進(jìn)入休眠(直到信號(hào)量的值>0)
//    如果信號(hào)量>0 就減1 然后繼續(xù)往下執(zhí)行后面的代碼
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//    讓信號(hào)量的值加1
    dispatch_semaphore_signal(semaphore);

3.8 @synchronized

  3.8.1 @synchronized 是對(duì)mutex遞歸鎖的封裝
  3.8.2 @synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖扇售,然后進(jìn)行加鎖前塔、解鎖操作
  3.8.3 示例代碼
@synchronized(obj){
}

練習(xí)代碼git

https://gitee.com/tianmingfu/thread-synchronization.git

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市承冰,隨后出現(xiàn)的幾起案子华弓,更是在濱河造成了極大的恐慌,老刑警劉巖困乒,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寂屏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)迁霎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)吱抚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人考廉,你說(shuō)我怎么就攤上這事秘豹。” “怎么了芝此?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵憋肖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我婚苹,道長(zhǎng)岸更,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任膊升,我火速辦了婚禮怎炊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘廓译。我一直安慰自己评肆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布非区。 她就那樣靜靜地躺著瓜挽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪征绸。 梳的紋絲不亂的頭發(fā)上久橙,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音管怠,去河邊找鬼淆衷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛渤弛,可吹牛的內(nèi)容都是我干的祝拯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼她肯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼佳头!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起晴氨,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤畜晰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瑞筐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體凄鼻,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腊瑟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了块蚌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闰非。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖峭范,靈堂內(nèi)的尸體忽然破棺而出财松,到底是詐尸還是另有隱情,我是刑警寧澤纱控,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布辆毡,位于F島的核電站,受9級(jí)特大地震影響甜害,放射性物質(zhì)發(fā)生泄漏舶掖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一尔店、第九天 我趴在偏房一處隱蔽的房頂上張望眨攘。 院中可真熱鬧,春花似錦嚣州、人聲如沸鲫售。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)情竹。三九已至,卻和暖如春匀哄,著一層夾襖步出監(jiān)牢的瞬間鲤妥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工拱雏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人底扳。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓铸抑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親衷模。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鹊汛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • 應(yīng)用程序里面多個(gè)線程的存在引發(fā)了多個(gè)執(zhí)行線程安全訪問(wèn)資源的潛在問(wèn)題。兩個(gè)線程同時(shí)修改同一資源有可能以意想不到的方式...
    搬磚的crystal閱讀 648評(píng)論 0 1
  • 1. 什么情況下會(huì)有線程隱患阱冶? 我們?cè)谑褂枚嗑€程技術(shù)帶來(lái)的便利的同時(shí)刁憋,也需要考慮下多線程所帶來(lái)的隱患。比如木蹬,我們可...
    沉江小魚(yú)閱讀 814評(píng)論 0 11
  • iOS 的鎖有以下 10 種:OSSpinLock至耻、os_unfair_lock、dispatch_semapho...
    阿斯蘭iOS閱讀 477評(píng)論 0 3
  • 一、iOS中常見(jiàn)的多線程方案 要解釋多線程尘颓,需要先解釋進(jìn)程和線程之間的區(qū)別走触。線程才是程序真正的執(zhí)行單元,一個(gè)進(jìn)程可...
    小豆豆苗閱讀 784評(píng)論 0 0
  • 深入淺出iOS多線程(一)——線程的概念深入淺出iOS多線程(二)——pthraed和NSThread的使用深入淺...
    struggle3g閱讀 1,133評(píng)論 0 5