iOS中鎖的總結(jié)

在多線程中,當(dāng)多個(gè)線程同時(shí)訪問同一塊資源的時(shí)候,就容易引起數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問題

(1).OSSpinLock

OSSpinLock叫做”自旋鎖”,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài),一直占用著CPU資源

存在問題:

  1. 目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問題

  2. 如果等待鎖的線程優(yōu)先級(jí)較高,它會(huì)一直占用著CPU資源,優(yōu)先級(jí)低的線程就無法釋放鎖

當(dāng)多個(gè)線程優(yōu)先級(jí)不同的時(shí)候,同時(shí)調(diào)用同一方法,當(dāng)優(yōu)先級(jí)低的線程先調(diào)用這個(gè)方法的時(shí)候,就會(huì)進(jìn)行加鎖,優(yōu)先級(jí)高的進(jìn)來發(fā)現(xiàn)已經(jīng)加鎖了,而CPU會(huì)對(duì)優(yōu)先級(jí)高的分配的時(shí)間片多,這時(shí)CPU會(huì)從優(yōu)先級(jí)低的切換到優(yōu)先級(jí)高的線程執(zhí)行,而優(yōu)先級(jí)低的線程沒執(zhí)行完,所以沒解鎖,優(yōu)先級(jí)高的就會(huì)一直訪問是否解鎖,所以就會(huì)一直卡著

// 初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
// 加鎖
OSSpinLockLock(&lock1);
// 解鎖
OSSpinLockUnlock(&lock1);

//還有一種嘗試加鎖,能加鎖就加,不能就直接跳過執(zhí)行之后代碼
bool result = OSSpinLockTry(&lock)

(2).os_unfair_lock
  1. os_unfair_lock用于取代不安全的OSSpinLock ,從iOS10開始才支持
  2. 從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài),并非忙等
  3. 需要導(dǎo)入頭文件#import <os/lock.h>
//初始化
os_unfair_loc lock = OS_UNFAIR_LOCK_INIT;
// 嘗試加鎖
os_unfair_lock_trylock(&lock)
// 加鎖
os_unfair_lock_lock(&lock);
// 解鎖
os_unfair_lock_unlock(&lock);
(3).pthread_mutex(互斥鎖、遞歸鎖)
  1. mutex叫做”互斥鎖”,等待鎖的線程會(huì)處于休眠狀態(tài)
  2. n需要導(dǎo)入頭文件#import <pthread.h>
 /*
 鎖的屬性的設(shè)置
 Mutex type attributes
 */
#define PTHREAD_MUTEX_NORMAL        0
#define PTHREAD_MUTEX_ERRORCHECK    1
#define PTHREAD_MUTEX_RECURSIVE     2 // 遞歸鎖,允許同一個(gè)線程對(duì)一把鎖進(jìn)行重復(fù)加鎖
#define PTHREAD_MUTEX_DEFAULT       PTHREAD_MUTEX_NORMAL  

// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化鎖
pthread_mutex_t mutex
pthread_mutex_init(mutex, &attr);
// 加鎖
pthread_mutex_lock(&mutex);
// 解鎖
pthread_mutex_unlock(&mutex);
// 銷毀相關(guān)資源
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&mutex);
(4).pthread_mutex– 遞歸鎖

和上面的一樣,只不過更換一下鎖的屬性的枚舉值

// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化鎖
pthread_mutex_t mutex
pthread_mutex_init(mutex, &attr);
(5).pthread_mutex– 條件

場(chǎng)景:兩條線程有依賴關(guān)系,一條線程必須等另外一天線程完成才執(zhí)行相關(guān)操作

// 初始化鎖
pthread_mutex_t mutex;
// NULL代表使用默認(rèn)屬性
pthread_mutex_init(&_mutex, NULL);

// 初始化條件
pthread_cond_t cond;
pthread_cond_init(&_cond, NULL);

// 等待條件(符合條件,進(jìn)入休眠,放開鎖;被喚醒后會(huì)再次加鎖)
pthread_cond_wait(&_cond, &_mutex);

// 激活一個(gè)等待該條件的線程
pthread_cond_signal(&_cond);
// 激活所有等待該條件的線程
pthread_cond_broadcast(&_cond);

// 銷毀資源
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);

(6).NSLock

NSLock是對(duì)pthread_mutex_t普通鎖的封裝

@interface NSLock : NSObject <NSLocking> {
  //嘗試加鎖,能加就加
- (BOOL)tryLock;
  //在limit之前一直等待加鎖,如果limit之前不能加鎖,則返回false,否則返回true
- (BOOL)lockBeforeDate:(NSDate *)limit;
}

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
  
//初始化
  NSLock *lock = [[NSLock alloc] init];
(7).NSRecursiveLock

NSRecursiveLock也是對(duì)mutex遞歸鎖的封裝,API跟NSLock基本一致

(8).NSCondition

NSCondition是對(duì)mutex和cond的封裝

@interface NSCondition : NSObject <NSLocking> {
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
}

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

(9).NSConditionLock

NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝,可以設(shè)置具體的條件值

@interface NSConditionLock : NSObject <NSLocking> {
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
}
(10).dispatch_queue

直接使用GCD的串行隊(duì)列,也是可以實(shí)現(xiàn)線程同步的

dispatch_queue_t queue = dispatch_queue_create("lock_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
       // 任務(wù)
    });
(11).dispatch_semaphore
  1. semaphore叫做”信號(hào)量”

  2. 信號(hào)量的初始值,可以用來控制線程并發(fā)訪問的最大數(shù)量

  3. 信號(hào)量的初始值為1,代表同時(shí)只允許1條線程訪問資源,保證線程同步

// 信號(hào)量的初始值
int value = 1;
// 初始化信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);
// 如果信號(hào)量的值<=0,當(dāng)前線程就會(huì)進(jìn)入休眠等待(知道信號(hào)量的值>0)
// 如果信號(hào)量的值>0,就減一,然后往下執(zhí)行代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 讓信號(hào)量的值加一
dispatch_semaphore_signal(semaphore);
(12).@synchronized
  1. @synchronized是對(duì)mutex遞歸鎖的封裝

  2. 源碼查看:objc4中的objc-sync.mm文件

  3. @synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖,然后進(jìn)行加鎖、解鎖操作

@synchronized(obj) {
        // 任務(wù)
    }
總結(jié)
  • OSSpinLock不再安全,底層用os_unfair_lock替代
  • atomic只能保證setter、getter時(shí)線程安全,所以更多的使用nonatomic來修飾
  • @synchronized在底層維護(hù)了一個(gè)哈希鏈表進(jìn)行data的存儲(chǔ),使用recursive_mutex_t進(jìn)行加鎖
  • NSLock、NSRecursiveLock、NSCondition和NSConditionLock底層都是對(duì)pthread_mutex的封裝
  • NSCondition和NSConditionLock是條件鎖,當(dāng)滿足某一個(gè)條件時(shí)才能進(jìn)行操作,和信號(hào)量dispatch_semaphore類似
  • 普通場(chǎng)景下涉及到線程安全,可以用NSLock
  • 循環(huán)調(diào)用時(shí)用NSRecursiveLock
  • 循環(huán)調(diào)用且有線程影響時(shí),請(qǐng)注意死鎖,如果有死鎖問題請(qǐng)使用@synchronized
自旋鎖、互斥鎖比較

什么情況使用自旋鎖比較劃算?

  1. 預(yù)計(jì)線程等待鎖的時(shí)間很短

  2. 加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用,但競(jìng)爭(zhēng)情況很少發(fā)生

  3. CPU資源不緊張

  4. 多核處理器

什么情況使用互斥鎖比較劃算?

預(yù)計(jì)線程等待鎖的時(shí)間較長(zhǎng)

  1. 單核處理器

  2. 臨界區(qū)有IO操作

  3. 臨界區(qū)代碼復(fù)雜或者循環(huán)量大

  4. 臨界區(qū)競(jìng)爭(zhēng)非常激烈

iOS線程同步方案性能比較(性能從高到低排序)

os_unfair_lock > OSSpinLock > dispatch_semaphore > pthread_mutex > dispatch_queue(DISPATCH_QUEUE_SERIAL) > NSLock > NSCondition > pthread_mutex(recursive) > NSRecursiveLock > NSConditionLock > @synchronized

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • OSSpinLock OSSpinLock自旋鎖,因?yàn)樽孕i一直busy-waiting忙等待占用cpu,且不會(huì)像...
    FengyunSky閱讀 1,740評(píng)論 0 2
  • 一. 多線程 1.常見多線程方案 pthread : 純粹 C 語言的API,跨平臺(tái), 線程生命周期程序員管理...
    天明天閱讀 4,271評(píng)論 0 14
  • 1、鎖的歸類 鎖的分類只有兩大類自旋鎖和和互斥鎖。這兩大類下又分成很多不同的小類。了解鎖之前建議先了解一下線程及線...
    希爾羅斯沃德_董閱讀 2,164評(píng)論 2 4
  • 前言 對(duì)于iOS中各種鎖的學(xué)習(xí)總結(jié),供日后查閱 引子 日常開發(fā)中,@property (nonatomic, st...
    Tr2e閱讀 981評(píng)論 1 1
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn),但是人生放棄了冒險(xiǎn),也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 7,541評(píng)論 0 4

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