iOS多線程中的各種鎖操作

我們?cè)谄綍r(shí)的開發(fā)過程中可能會(huì)遇到多個(gè)線程并發(fā)同時(shí)訪問同一資源的情況,例如數(shù)據(jù)庫的存取操作,或者是文件的讀寫操作,像這種多個(gè)線程同時(shí)訪問同一資源就會(huì)容易產(chǎn)生資源競(jìng)爭(zhēng)袭厂,導(dǎo)致數(shù)據(jù)錯(cuò)亂的問題,解決這種問題我們一般會(huì)選擇使用線程同步技術(shù)球匕,也就是對(duì)這同一資源進(jìn)行加鎖解鎖操作纹磺,iOS中的鎖主要有以下幾種:

  • OSSpinLock(在iOS10已經(jīng)廢棄了)
  • os_unfair_lock
  • pthread_mutex_t
  • pthread_cond_t
  • NSLock
  • NSConditionLock
  • @synchronized
  • dispatch_semaphore_t:信號(hào)量也可以作為鎖來使用
  • dispatch_sync(DISPATCH_QUEUE_SERIAL):使用串行同步隊(duì)列也可以作為鎖來使用
image
image
image
image

下面我們就逐一對(duì)上面列出的鎖進(jìn)行簡單地使用示例說明

OSSpinLock基本用法:

@interface OSSpinLockDemo ()

@property (nonatomic, assign) OSSpinLock ossLock;
@end

@implementation OSSpinLockDemo

- (instancetype)init {
    if (self = [super init]) {
            // 初始化自旋鎖
        _ossLock = OS_SPINLOCK_INIT;
    }
    return self;
}

- (void)test {
    
    // 加鎖
    OSSpinLockLock(&_ossLock);
    
    // 需要加鎖的代碼放在加鎖和解鎖語句中間,例如下面的NSLog打印
    NSLog(@"加鎖代碼");
    
    // 解鎖
    OSSpinLockUnlock(&_ossLock);
}
@end
image

os_unfair_lock基本用法:

@interface OSUnfairLockDemo ()

@property (nonatomic, assign) os_unfair_lock lock;
@end

@implementation OSUnfairLockDemo

- (instancetype)init {
    if (self = [super init]) {
        // 初始化鎖
        _lock = OS_UNFAIR_LOCK_INIT;
    }
    return self;
}

- (void)test {
    // 加鎖
    os_unfair_lock_lock(&_lock);
    
    // 需要加鎖的代碼
    
    // 解鎖
    os_unfair_lock_unlock(&_lock);
}
@end
image

pthread_mutex_t基本用法:

@interface pthreadMutexLockDemo ()
    
@property (nonatomic, assign) pthread_mutex_t mutex;
@end

@implementation pthreadMutexLockDemo

- (instancetype)init {
    if (self = [super init]) {
        // 初始化屬性
//        pthread_mutex_t attr;
//        pthread_mutexattr_init(&attr);
            // PTHREAD_MUTEX_DEFAULT:是普通互斥鎖
            // PTHREAD_MUTEX_RECURSIVE:遞歸鎖
//        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
//
//        // 初始化鎖
//        pthread_mutex_init(&_mutex, &attr);
//
//        // 銷毀屬性
//        pthread_mutexattr_destroy(&attr);
        
        // 最簡單的初始化方式是第二個(gè)參數(shù)直接填NULL即可
        pthread_mutex_init(&_mutex, NULL);
    }
    return self;
}

- (void)test {
    // 加鎖
    pthread_mutex_lock(&_mutex);
    
    // 需要加鎖的代碼
    
    // 解鎖
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc {
    // 銷毀鎖
    pthread_mutex_destroy(&_mutex);
}
@end
image

pthread_cond_t基本使用:

@interface pthreadMutexConditionLockDemo ()
    
@property (nonatomic, assign) pthread_mutex_t mutex;
@property (nonatomic, assign) pthread_cond_t con;
@property (nonatomic, strong) NSMutableArray *data;
@end

@implementation pthreadMutexConditionLockDemo

- (instancetype)init {
    if (self = [super init]) {
        // pthread_mutex_init 第二個(gè)參數(shù)可以參NULL
        pthread_mutex_init(&_mutex, NULL);
        pthread_cond_init(&_con, NULL);
        
        _data = [NSMutableArray array];
    }
    return self;
}

- (void)test {
    [[[NSThread alloc] initWithTarget:self selector:@selector(remove) object:nil] start];
    
    sleep(2);
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(add) object:nil] start];
}

- (void)remove {
    // 加鎖
    pthread_mutex_lock(&_mutex);
    
    if (!self.data.count) {
        // 線程等待
        pthread_cond_wait(&_con, &_mutex);
    }
    
    [self.data removeLastObject];
    NSLog(@"刪除元素");
    
    // 解鎖
    pthread_mutex_unlock(&_mutex);
}

- (void)add {
    // 加鎖
    pthread_mutex_lock(&_mutex);
    
    [self.data addObject:@"111"];
    NSLog(@"添加元素");
    
    // 喚醒等待的線程
    pthread_cond_signal(&_con);
    
    // 解鎖
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_con);
}
@end
image

NSLock基本用法:

@interface NSLockDemo ()

@property (nonatomic, strong) NSLock *lock;
@end

@implementation NSLockDemo

- (instancetype)init {
    if (self = [super init]) {
            // 初始化鎖
        _lock = [[NSLock alloc] init];
    }
    return self;
}

- (void)test {
     // 加鎖
    [self.lock lock];
    
    // 加鎖的代碼
    
    // 解鎖
    [self.lock unlock];
}
@end
image

NSConditionLock:基本用法

@interface NSConditionLockDemo ()
    
@property (nonatomic, strong) NSConditionLock *conLock;
@end

@implementation NSConditionLockDemo

- (instancetype)init {
    if (self = [super init]) {
        // 初始化鎖
        _conLock = [[NSConditionLock alloc] initWithCondition:1];
    }
    return self;
}

- (void)test {
    [[[NSThread alloc] initWithTarget:self selector:@selector(remove) object:nil] start];
    
    sleep(2);
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(add) object:nil] start];
}

- (void)remove {
    // 加鎖
    [self.conLock lockWhenCondition:1];
    
    NSLog(@"刪除元素");
    
    // 解鎖
    [self.conLock unlockWithCondition:2];
}

- (void)add {
    // 加鎖
    [self.conLock lockWhenCondition:2];
    
    NSLog(@"添加元素");

    // 解鎖
    [self.conLock unlock];
}
@end
image

@synchronized基本用法:

@implementation synchronizedLockDemo

- (void)test {
    // 根據(jù)傳遞的slef生成對(duì)應(yīng)的鎖亮曹,傳入不同的對(duì)象生成不同的鎖橄杨,底層是mutex 遞歸鎖
    @synchronized (self) {
        // 需要加鎖的代碼
    }
}
@end
image

dispatch_semaphore_t基本用法:

@interface semaphoneLockDemo ()

@property (nonatomic, strong) dispatch_semaphore_t semaphone;
@end

@implementation semaphoneLockDemo

- (instancetype)init {
    if (self = [super init]) {
            // 初始化信號(hào)量秘症,1表示同時(shí)只能有1個(gè)線程來執(zhí)行任務(wù)
        _semaphone = dispatch_semaphore_create(1);
    }
    return self;
}

- (void)test {
    for (NSInteger i = 0; i < 20; i ++) {
        [[[NSThread alloc] initWithTarget:self selector:@selector(doTask) object:nil] start];
    }
}

- (void)doTask {
    
    // 如果信號(hào)量的值>0,信號(hào)量的值就-1式矫,然后執(zhí)行后面的代碼乡摹,DISPATCH_TIME_FOREVER:一直等
    // 如果信號(hào)量的值<=0,此時(shí)就進(jìn)入休眠采转,等待信息量的值變成>0
    dispatch_semaphore_wait(self.semaphone, DISPATCH_TIME_FOREVER);
    
    sleep(2);
    NSLog(@"1212");
    
    // 讓信號(hào)量的值+1
    dispatch_semaphore_signal(self.semaphone);
}
@end
image

dispatch_sync(DISPATCH_QUEUE_SERIAL)基本用法:

@interface SerialQueueLockDemo ()

@property (nonatomic, strong) dispatch_queue_t ticketQueue;
@end

@implementation SerialQueueLockDemo

- (instancetype)init {
    if (self = [super init]) {
        _tickeetQueue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)test {
    dispatch_sync(self.tickeetQueue, ^{
        // 加鎖的代碼
    });
}
@end
image

上面我們列舉了iOS中的幾種鎖聪廉,但是有的鎖已經(jīng)廢棄了,有的鎖性能上存在些問題故慈,那么我們?cè)谄綍r(shí)的開發(fā)中會(huì)選用哪種鎖來使用比較合適尼板熊,這里根據(jù)大量的三方框架和蘋果底層框架中使用到的鎖的情況來看,NSLock察绷,pthread_mutex干签,dispatch_semaphore是出現(xiàn)頻次最多的

image
image

上面我們介紹的這些鎖對(duì)于多個(gè)異步線程同時(shí)訪問同一資源時(shí),使用這些鎖來進(jìn)行線程同步確實(shí)是可以解決問題拆撼,但是如果我們是對(duì)文件進(jìn)行讀寫操作容劳,這時(shí)上面的鎖就不太適合使用了,因?yàn)槲募x寫操作的特點(diǎn)是可以同時(shí)讀情萤,但是不能同時(shí)寫,也不能在讀的時(shí)候進(jìn)行寫操作摹恨,也不能在寫的時(shí)候進(jìn)行讀操作筋岛,所以就有了下面的兩種專門處理文件讀寫操作的鎖:

  • pthread_rwlock_t
  • dispatch_barrier_async:異步柵欄也可以解決文件讀寫操作
image

pthread_rwlock_t基本用法:

@interface pthread_rwlockDemo ()

@property (nonatomic, assign) pthread_rwlock_t rwlock;
@end

@implementation pthread_rwlockDemo

- (void)test {
    // 初始化鎖
    pthread_rwlock_init(&_rwlock, NULL);
    
    for (NSInteger i = 0; i < 10; i++) {
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self read];
        });
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self write];
        });
    }
}

- (void)read {
    
    // 讀加鎖
    pthread_rwlock_rdlock(&_rwlock);
    
    sleep(1);
    
    NSLog(@"開始讀");
    
    // 解鎖
    pthread_rwlock_unlock(&_rwlock);
}

- (void)write {
    // 寫加鎖
    pthread_rwlock_wrlock(&_rwlock);
    
    sleep(1);
    
    NSLog(@"開始寫");
    
    // 解鎖
    pthread_rwlock_unlock(&_rwlock);
}

- (void)dealloc {
    // 銷毀鎖
    pthread_rwlock_destroy(&_rwlock);
}
@end
image

dispatch_barrier_async:基本用法:

@interface dispatch_barrier_asyncDemo ()

@property (nonatomic, strong) dispatch_queue_t queue;
@end

@implementation dispatch_barrier_asyncDemo

- (void)test {
    // 創(chuàng)建一個(gè)并發(fā)隊(duì)列,注意:這里使用dispatch_barrier_async只能使用`dispatch_queue_create`創(chuàng)建一個(gè)并發(fā)隊(duì)列晒哄,不能使用全局并發(fā)隊(duì)列
    self.queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    for (NSInteger i = 0; i < 10; i ++) {
        [self read];
        [self read];
        [self read];
        [self read];
        [self write];
    }
}

- (void)read {
    dispatch_async(self.queue, ^{
        sleep(1);
        NSLog(@"開始讀");
    });
}

- (void)write {
    dispatch_barrier_async(self.queue, ^{
        // 開始加鎖
        sleep(1);
        NSLog(@"開始寫");
    });
}
@end
image

講解示例Demo地址:https://github.com/guangqiang-liu/09.1-ThreadLock

更多文章

  • ReactNative開源項(xiàng)目OneM(1200+star):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
  • iOS組件化開發(fā)實(shí)戰(zhàn)項(xiàng)目(500+star):https://github.com/guangqiang-liu/iOS-Component-Pro:歡迎小伙伴們 star
  • 簡書主頁:包含多篇iOS和RN開發(fā)相關(guān)的技術(shù)文章http://www.reibang.com/u/023338566ca5 歡迎小伙伴們:多多關(guān)注睁宰,點(diǎn)贊
  • ReactNative QQ技術(shù)交流群(2000人):620792950 歡迎小伙伴進(jìn)群交流學(xué)習(xí)
  • iOS QQ技術(shù)交流群:678441305 歡迎小伙伴進(jìn)群交流學(xué)習(xí)
最后編輯于
?著作權(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)離奇詭異,居然都是意外死亡伐债,警方通過查閱死者的電腦和手機(jī)预侯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峰锁,“玉大人萎馅,你說我怎么就攤上這事『缃” “怎么了糜芳?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵飒货,是天一觀的道長。 經(jīng)常有香客問我峭竣,道長塘辅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任邪驮,我火速辦了婚禮莫辨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘毅访。我一直安慰自己沮榜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布喻粹。 她就那樣靜靜地躺著蟆融,像睡著了一般。 火紅的嫁衣襯著肌膚如雪守呜。 梳的紋絲不亂的頭發(fā)上型酥,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音查乒,去河邊找鬼弥喉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛玛迄,可吹牛的內(nèi)容都是我干的由境。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼蓖议,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼虏杰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起勒虾,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤纺阔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后修然,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笛钝,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有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
  • 文/蒙蒙 一醋拧、第九天 我趴在偏房一處隱蔽的房頂上張望慷嗜。 院中可真熱鬧,春花似錦丹壕、人聲如沸庆械。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缭乘。三九已至,卻和暖如春琉用,著一層夾襖步出監(jiān)牢的瞬間堕绩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工邑时, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奴紧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓晶丘,卻偏偏與公主長得像黍氮,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子铣口,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353