iOS 鎖 部分一

主要講解OSSpinLock/os_unfair_lock/pthread_mutex_t鎖的基本用法

常見鎖的分類:

  • 自旋鎖OSSpinLock
  • 互斥鎖os_unfair_lock
  • 互斥/遞歸/條件鎖pthread_mutex_t
  • 互斥鎖NSLock
  • 遞歸鎖NSRecursiveLock
  • 條件鎖NSCondition
  • 條件鎖NSConditionLock
  • 遞歸鎖 @synchronized
  • 信號量semaphore
  • 讀寫鎖pthread_rwlock_t
  • 異步柵欄dispatch_barrier_async

iOS 鎖 部分一
iOS 鎖 部分二
iOS 鎖 部分三
iOS 鎖 部分四


1. 自旋鎖 OSSpinLock

特點:

  1. 不是一個安全的鎖, 等待鎖的線程會處于忙等(busy-wait)狀態(tài), 一直占用著CPU資源; (類似一個while(1)循環(huán)一樣,不停的查詢鎖的狀態(tài), 注意區(qū)分Runloop的機(jī)制, 同樣是阻塞, 但是Runloop是類似休眠的阻塞, 不會耗費(fèi)CPU資源, 自旋鎖的這種忙等機(jī)制使它相比其他鎖效率更高, 因為沒有喚醒-休眠這些類似操作, 從而能更快去處理事情);

注意OSSpinLock自旋鎖iOS10之后已經(jīng)廢棄使用, 它可能會導(dǎo)致優(yōu)先級反轉(zhuǎn); 例如A/B兩個線程, A的優(yōu)先級大于B的, 我們的本意是A的任務(wù)優(yōu)先執(zhí)行; 但是使用OSSpinLock后, 如果是B先獲得了優(yōu)先訪問了共享資源獲得了鎖并加鎖, 而A線程再去訪問共享資源時鎖就會處于忙等狀態(tài), 由于優(yōu)先級高則它會一直占用CPU資源不會讓出時間片;這樣B一直不能獲得CPU資源去執(zhí)行任務(wù), 導(dǎo)致無法完成;

  1. 使用時需要導(dǎo)入頭文件#import <libkern/OSAtomic.h>
  2. 使用時可能會用到的方法:
    3.1 OS_SPINLOCK_INIT初始化自旋鎖;
    3.2 OSSpinLockLock()加鎖;
    3.3 OSSpinLockUnlock()解鎖;
    3.4 OSSpinLockTry()嘗試加鎖, 返回一個BOOL值;
  3. 示例代碼如下
#import "ViewController1.h"
#import <libkern/OSAtomic.h>
@interface ViewController1 ()
@property (nonatomic, assign) NSInteger money;
@property (nonatomic, assign) OSSpinLock lock;
@end

@implementation ViewController1
- (void)viewDidLoad {
    [super viewDidLoad];
    self.money = 100;
    self.lock = OS_SPINLOCK_INIT;
    __weak typeof(self) weakSelf = self;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf saveMoney];
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf drawMoney];
        }
    });
}
- (void)saveMoney{
    //加鎖
    OSSpinLockLock(&_lock);
    [super saveMoney];
    //解鎖
    OSSpinLockUnlock(&_lock);
}
- (void)drawMoney {
    //加鎖
    OSSpinLockLock(&_lock);
    [super drawMoney ]; 
    //解鎖
    OSSpinLockUnlock(&_lock);
}
@end

2. 互斥鎖 os_unfair_lock

特點

  1. 設(shè)計宗旨在于替換OSSpinLock, 從 iOS10之后開始支持; 跟OSSpinLock不同, 等待os_unfari_lock的線程會處于休眠狀態(tài)(類似Runloop那這樣), 不是忙等;
  2. 使用時需要引入頭文件#import <os/lock.h>
  3. 使用時可能會用到的方法
    3.1 OS_UNFAIR_LOCK_INIT初始化鎖;
    3.2 os_unfair_lock_lock()加鎖;
    3.3 os_unfair_lock_unlock()解鎖;
    3.4 os_unfair_lock_trylock()嘗試加鎖, 返回一個BOOL值;
  4. 測試代碼
#import "ViewController2.h"
#import <os/lock.h>
@interface ViewController2 ()
@property (nonatomic, assign) NSInteger money;
@property (nonatomic, assign) os_unfair_lock  lock;
@end
@implementation ViewController2
- (void)viewDidLoad {
    [super viewDidLoad];
    self.money = 100;
    self.lock = OS_UNFAIR_LOCK_INIT;
    __weak typeof(self) weakSelf = self;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf saveMoney];
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf drawMoney];
        }
    });
}
- (void)saveMoney{
    os_unfair_lock_lock(&_lock);
    [super saveMoney];
    os_unfair_lock_unlock(&_lock);
}
- (void)drawMoney {
    os_unfair_lock_lock(&_lock);
    [super drawMoney ];
    os_unfair_lock_unlock(&_lock);
}
@end

3. 互斥/遞歸/條件鎖 pthread_mutex_t

特點

  1. 跨平臺使用的API, 等待鎖的線程會處于休眠狀態(tài);
    當(dāng)使用遞歸鎖時: 允許同一個線程重復(fù)進(jìn)行加鎖(另一個線程訪問時就會等待), 這樣可以保證多線程時訪問共用資源的安全性;
  2. 使用時需要引入頭文件#import <pthread.h>
  3. 使用時可能會用到的方法
    3.1 鎖的類型有
    ///普通互斥鎖
  #define PTHREAD_MUTEX_NORMAL      0
   ///錯誤檢查鎖(不清楚什么用)
  #define PTHREAD_MUTEX_ERRORCHECK  1
   ///遞歸鎖
  #define PTHREAD_MUTEX_RECURSIVE       2
  ///等同PTHREAD_MUTEX_NORMAL
  #define PTHREAD_MUTEX_DEFAULT     PTHREAD_MUTEX_NORMAL

3.2 鎖的初始化方法:

   ///初始化屬性
   pthread_mutexattr_t att ;
   pthread_mutexattr_init(&att);
   ///設(shè)置鎖的屬性
   pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
   ///初始化鎖
   pthread_mutex_init(&_lock, &att);

3.2 pthread_mutex_lock()加鎖
3.3 pthread_mutex_unlock()解鎖
3.4 pthread_mutexattr_destroy()銷毀屬性;
3.5 pthread_mutex_destroy()銷毀鎖;
3.6 pthread_cond_wait()等待條件(進(jìn)入休眠的同時放開 mutex 鎖, 被喚醒的同時再次對 mutex 加鎖)
3.7 pthread_cond_signal()發(fā)送信號激活等待該條件的線程;
3.8 pthread_cond_broadcast()發(fā)送廣播信號激活等待該條件的所有線程;

  1. 關(guān)于互斥鎖遞歸鎖的測試代碼:
#import "ViewController3.h"
 #import <pthread.h>
@interface ViewController3 ()
@property (nonatomic, assign) NSInteger money;
@property (nonatomic, assign) pthread_mutex_t  lock;
@property (nonatomic, assign) pthread_mutex_t  recursiveLock;
@end
@implementation ViewController3
- (void)viewDidLoad {
    [super viewDidLoad];    
/******************************互斥鎖驗證******************************/
    self.money = 100;
        ///初始化屬性
    pthread_mutexattr_t att ;
    pthread_mutexattr_init(&att);
    ///設(shè)置鎖的屬性
    pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
    ///初始化鎖
    pthread_mutex_init(&_lock, &att);
    ///銷毀屬性
     pthread_mutexattr_destroy(&att);
    [self hanleMoney];
/******************************遞歸鎖驗證******************************/
    ///初始化屬性
    pthread_mutexattr_t recursiveAtt ;
    pthread_mutexattr_init(&recursiveAtt);
    ///設(shè)置鎖的屬性
    pthread_mutexattr_settype(&recursiveAtt, PTHREAD_MUTEX_RECURSIVE);
    ///初始化鎖
    pthread_mutex_init(&_recursiveLock, &recursiveAtt);
    ///銷毀屬性
    pthread_mutexattr_destroy(&recursiveAtt);
    [self recuresiveAction];
}
- (void)hanleMoney {
    __weak typeof(self) weakSelf = self;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf saveMoney];
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf drawMoney];
        }
    });
}
- (void)saveMoney{
    pthread_mutex_lock(&_lock);
    [super saveMoney];
    pthread_mutex_unlock(&_lock);
}
- (void)drawMoney {
    pthread_mutex_lock(&_lock);
    [super drawMoney ];
    pthread_mutex_unlock(&_lock);
}
/*************************************遞歸鎖********************************************/
- (void)recuresiveAction {
    static int count = 10;
    pthread_mutex_lock(&_recursiveLock);
    NSLog(@"count: %d", count);
    if (count > 0) {
        count --;
        [self recuresiveAction];
    }
    pthread_mutex_unlock(&_recursiveLock);
}
/*************************************遞歸鎖********************************************/
- (void)dealloc {
    pthread_mutex_destroy(&_lock);
    pthread_mutex_destroy(&_recursiveLock);
}
@end
  1. 關(guān)于條件鎖的驗證代碼
    首先設(shè)定以下使用場景, 兩條線程 AB, A線程中執(zhí)行刪除數(shù)組元素, B線程中執(zhí)行添加數(shù)組元素;由于不知道哪個線程會先執(zhí)行, 所以需要加鎖實現(xiàn), 只有在添加后才能執(zhí)行刪除操作;為互斥鎖添加條件可以實現(xiàn);
    通過此方法可以實現(xiàn)線程依賴;
#import "ViewController4.h"
#import <pthread.h>

@interface ViewController4 ()
@property (nonatomic, strong) NSMutableArray *dataArr;
@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, assign) pthread_cond_t condition;
@end
@implementation ViewController4
- (void)viewDidLoad {
    [super viewDidLoad];
    /**
     首先設(shè)定以下使用場景, 兩條線程 A和B, A線程中執(zhí)行刪除數(shù)組元素, B 線程中執(zhí)行添加數(shù)組元素;
     由于不知道哪個線程會先執(zhí)行, 所以需要加鎖實現(xiàn), 只有在添加后才能執(zhí)行刪除操作;為互斥鎖添加條件可以實現(xiàn);
     */
    ///初始化鎖
    pthread_mutexattr_t att;
    pthread_mutexattr_init(&att);
    pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
    pthread_mutex_init(&_lock, &att);
    pthread_mutexattr_destroy(&att);
    ///初始化條件
    pthread_cond_init(&_condition, NULL);
    NSThread *deThread = [[NSThread alloc] initWithTarget:self selector:@selector(deleteObj) object:nil];
    [deThread start];
    ///sleep一秒. 確保刪除元素的線程先獲得鎖;
    sleep(1);
    NSThread *adThread = [[NSThread alloc] initWithTarget:self selector:@selector(addObj) object:nil];
    [adThread start];
    
}
- (void)deleteObj {
    pthread_mutex_lock(&_lock);
    NSLog(@"delete begin");
    ///添加判斷, 如果沒有數(shù)據(jù)則添加條件
    if (self.dataArr.count < 1) {
        /**
         添加條件, 如果數(shù)組為空, 則添加等待線程休眠, 將鎖讓出;  接受到信號時會再次加鎖, 然后繼續(xù)向下執(zhí)行;
         */
        pthread_cond_wait(&_condition, &_lock);
    }
    
    [self.dataArr removeLastObject];
    NSLog(@"數(shù)組執(zhí)行刪除元素操作");
    pthread_mutex_unlock(&_lock);
}
- (void)addObj {
    pthread_mutex_lock(&_lock);
    NSLog(@"add begin");
    [self.dataArr addObject:@"HTI"];
    ///發(fā)送新號, 說明已經(jīng)添加元素了;
    pthread_cond_signal(&_condition);
    NSLog(@"數(shù)組執(zhí)行添加元素操作");
    pthread_mutex_unlock(&_lock);
}
- (void)dealloc {
    pthread_mutex_destroy(&_lock);
    pthread_cond_destroy(&_condition);
}
@end

補(bǔ)充. 為什么會出現(xiàn)死鎖?

當(dāng)兩個線程中其中一個線程獲得鎖并加鎖后, 如果使用完沒有解鎖, 則另一個線程就會一直處于等待解鎖狀態(tài), 就會形成死鎖;


文中測試代碼
參考文章
不再安全的OSSpinLock
objc4源碼下載地址
libplatform源碼下載地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秉馏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子脱羡,更是在濱河造成了極大的恐慌萝究,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锉罐,死亡現(xiàn)場離奇詭異帆竹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)氓鄙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來业舍,“玉大人抖拦,你說我怎么就攤上這事∠夏海” “怎么了态罪?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長下面。 經(jīng)常有香客問我复颈,道長,這世上最難降的妖魔是什么沥割? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任耗啦,我火速辦了婚禮凿菩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘帜讲。我一直安慰自己衅谷,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布似将。 她就那樣靜靜地躺著获黔,像睡著了一般箱玷。 火紅的嫁衣襯著肌膚如雪真屯。 梳的紋絲不亂的頭發(fā)上衷蜓,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天颜阐,我揣著相機(jī)與錄音纫骑,去河邊找鬼塔嬉。 笑死独榴,一個胖子當(dāng)著我的面吹牛项鬼,可吹牛的內(nèi)容都是我干的侦厚。 我是一名探鬼主播耻陕,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼刨沦!你這毒婦竟也來了诗宣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤想诅,失蹤者是張志新(化名)和其女友劉穎召庞,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體来破,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡篮灼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了徘禁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诅诱。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖送朱,靈堂內(nèi)的尸體忽然破棺而出娘荡,到底是詐尸還是另有隱情,我是刑警寧澤驶沼,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布炮沐,位于F島的核電站,受9級特大地震影響回怜,放射性物質(zhì)發(fā)生泄漏大年。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翔试。 院中可真熱鬧轻要,春花似錦、人聲如沸遏餐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽失都。三九已至柏蘑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間粹庞,已是汗流浹背咳焚。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留庞溜,地道東北人革半。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像流码,于是被迫代替她去往敵國和親又官。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344