iOS 鎖 Lock 線程同步 多線程安全隱患

多線程的安全隱患

多條線程操作同一個資源,會造成資源的不同步,造成數(shù)據(jù)的不準確。
采用線程同步技術來規(guī)避資源的同步,常用的技術就是加鎖

鎖保證當前情況下只能有一個線程操作該資源,操作完解鎖,后面線程進來繼續(xù)加鎖解鎖。
iOS 中線程同步的方案有

  • OSSpinLock(自旋鎖)

等待鎖的線程會處于忙等(busy-wait)狀態(tài),一直占用著CPU資源,
目前已經(jīng)不安全了,會出現(xiàn)線程優(yōu)先級反轉的問題。

優(yōu)先級反轉:
thread1優(yōu)先級高,thread2低,當執(zhí)行任務時,比如thread2加鎖,執(zhí)行thread1會在鎖處忙等,等待thread2解鎖,但是thread1的優(yōu)先級比較高,CPU會分配給thread1的時間調(diào)度比較多,導致thread2一直無法放開鎖,造成類似于死鎖的現(xiàn)象

#import <libkern/OSAtomic.h>
/**
 自旋鎖
 */
@property (nonatomic, assign) OSSpinLock lock;
- (void)use {
   //初始化
    self.lock = OS_SPINLOCK_INIT;
    //加鎖
    OSSpinLockLock(&_lock); 
   //解鎖
    OSSpinLockUnlock(&_lock);
}

  • os_unfair_lock

os_unfair_lock 用于取代不安全的OSSpinLock,從iOS10 開始支持,從底層來看,等待os_unfair_lock鎖的線程會處于休眠狀態(tài),也不是忙等狀態(tài)。

#import <os/lock.h>
@property (nonatomic, assign) os_unfair_lock lock;
- (void)use {
   //初始化
    self.lock = OS_UNFAIR_LOCK_INIT;
    //加鎖
    os_unfair_lock_lock(&_lock); 
   //解鎖
    os_unfair_lock_unlock(&_lock);
}
  • pthread_mutex

pthread是跨平臺的鎖 ,互斥鎖,等待所得線程會處于休眠狀態(tài)。
初始化的時候創(chuàng)建屬性可以選擇鎖類型:
PTHREAD_MUTEX_NORMAL //普通鎖
PTHREAD_MUTEX_RECURSIVE //遞歸鎖,用于遞歸調(diào)用的時候避免產(chǎn)生死鎖情況。

#import <pthread.h>
@property (nonatomic, assign) pthread_mutex_t lock;
- (void)use {
   //初始化
   [self __initLock:&_lock];
    //加鎖
    pthread_mutex_lock(&_lock); 
   //解鎖
    pthread_mutex_unlock(&_lock);
    //銷毀
    pthread_mutex_destroy(&_lock);
}
- (void) __initLock:(pthread_mutex_t *)lock {
    //PTHREAD_MUTEX_INITIALIZER是個結構體,語法不允許這么創(chuàng)建
    //        self.ticketLock = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutexattr_t att;
    pthread_mutexattr_init(&att);
    pthread_mutexattr_settype(&att, PTHREAD_MUTEX_NORMAL);
    pthread_mutex_init(lock, &att);
//    pthread_mutex_init(lock, NULL); //也可以穿NULL,采用默認
    //銷毀屬性
    pthread_mutexattr_destroy(&att);
}

還有一種(pthread_mutex-條件)用法,改方法可以實現(xiàn)線程之間相互以依賴的問題,比如線程1加鎖之后,需要依賴線程2執(zhí)行完之后才能夠進行。此時使用pthread_cond_t 可以給pthread_mutex_lock添加條件,來進行多線程之間的控制。


互斥鎖條件用法
  • NSLock

NSLock是對mutex普通鎖的封裝。

- (BOOL)tryLock;  //嘗試加鎖,未加鎖-->加鎖,加鎖-->繼續(xù)執(zhí)行
- (BOOL)lockBeforeDate:(NSDate *)limit;
//時間段內(nèi)加鎖,limit時間之前阻塞線程,等到鎖斷開之后或者未加鎖狀態(tài)下加鎖成功返回YES,否則加鎖失敗 返回NO。
  • NSRecursiveLock

遞歸鎖,也是對mute遞歸鎖的封裝。
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_RECURSIVE);

  • NSCondition

條件鎖,也是對mute和pthread_cond_t的封裝??梢哉{(diào)用wait 方法,等待條件成立再執(zhí)行鎖一下的內(nèi)容。signle方法喚醒線程鎖。

  • NSConditionLock

是對NSCondition進一步封裝,可以設定具體的條件值,首先初始化一個條件值,
當執(zhí)行[self.conditionLock lockWhenCondition:1];只有條件值對應上才能加鎖成功,否則一直等待鎖得狀態(tài)。
[self.conditionLock unlockWithCondition:2];解鎖并且將條件值賦值為2

  • dispatch_semaphore

dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
信號量的值>0的時候執(zhí)行,<=0等待
dispatch_semaphore_signal(self.semaphore);
信號量的值+1

  • dispatch_queue(DISPATCH_QUEUE_SERIAL)

GCD的串行隊列,也可以實現(xiàn)鎖的概念。

  • @synchronized

@synchronized是對mutex遞歸鎖得封裝
源碼在objc4/sync.mm 內(nèi)部.

@synchronized(self){
       //lock content
    }

@synchronized需要傳入一個對象,這個對象(不一定就是self)就是這把鎖,相同對象持有相同鎖,不同對象持有不同鎖,關鍵字在左大括號加鎖,大括號結束解鎖。底層就是對mutex遞歸鎖的封裝。

不同鎖的優(yōu)劣

鎖大致分為2種

  • OSSpinLock(自旋鎖),加鎖狀態(tài)的時候線程處于忙等狀態(tài),占用CPU資源。
  • pthread_mutex(互斥鎖),跨平臺,加鎖狀態(tài)的時候線程處于休眠狀態(tài),不會占用CPU資源。


    各個鎖性能比較

面試題

  1. 什么情況使用自旋鎖比較劃算
  • 預計線程等待鎖的時間很短
  • 加鎖的代碼(臨界區(qū))會經(jīng)常調(diào)用,但競爭情況很少發(fā)生。
  • CPU資源不緊張
  • 多核處理器
  1. 什么情況使用互斥鎖比較劃算
  • 預計線程等待鎖的時間比較長
  • 單核處理器,可以休眠
  • 臨界區(qū)有IO(文件讀取)操作
  • 臨界區(qū)代碼比較復雜,或者循環(huán)量大
  • 競爭非常激烈
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 鎖是一種同步機制,用于多線程環(huán)境中對資源訪問的限制iOS中常見鎖的性能對比圖(摘自:ibireme): iOS鎖的...
    LiLS閱讀 1,644評論 0 6
  • 前言 一塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源,比如多個線程訪問同一個對象、同一個變量、同...
    WQ_UESTC閱讀 1,007評論 0 5
  • 一、前言 前段時間看了幾個開源項目,發(fā)現(xiàn)他們保持線程同步的方式各不相同,有@synchronized、NSLock...
    稻春閱讀 510評論 0 0
  • 轉載自:http://m.itdecent.cn/p/938d68ed832c# 一、前言 前段時間看了幾個...
    cafei閱讀 4,604評論 1 12
  • 線程安全是怎么產(chǎn)生的 常見比如線程內(nèi)操作了一個線程外的非線程安全變量,這個時候一定要考慮線程安全和同步。 - (v...
    幽城88閱讀 777評論 0 0

友情鏈接更多精彩內(nèi)容