iOS 常用的鎖(Lock)講解

轉(zhuǎn)自: iOS 常見知識點(三):Lock

NSLock

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

@interface NSLock : NSObject <NSLocking> {
@private
    void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end

NSLock 遵循 NSLocking 協(xié)議,lock 方法是加鎖,unlock 是解鎖,tryLock 是嘗試加鎖,如果失敗的話返回 NO,lockBeforeDate: 是在指定Date之前嘗試加鎖,如果在指定時間之前都不能加鎖,則返回NO。

//主線程中
NSLock *lock = [[NSLock alloc] init];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    NSLog(@"線程1");
    sleep(2);
    [lock unlock];
    NSLog(@"線程1解鎖成功");
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);//以保證讓線程2的代碼后執(zhí)行
    [lock lock];
    NSLog(@"線程2");
    [lock unlock];
});

2016-08-19 14:23:09.659 ThreadLockControlDemo[1754:129663] 線程1
2016-08-19 14:23:11.663 ThreadLockControlDemo[1754:129663] 線程1解鎖成功
2016-08-19 14:23:11.665 ThreadLockControlDemo[1754:129659] 線程2

線程 1 中的 lock 鎖上了,所以線程 2 中的 lock 加鎖失敗,阻塞線程 2,但 2 s 后線程 1 中的 lock 解鎖,線程 2 就立即加鎖成功,執(zhí)行線程 2 中的后續(xù)代碼。

//主線程中
NSLock *lock = [[NSLock alloc] init];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    NSLog(@"線程1");
    sleep(10);
    [lock unlock];
});

//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);//以保證讓線程2的代碼后執(zhí)行
    if ([lock tryLock]) {
        NSLog(@"線程2");
        [lock unlock];
    } else {
        NSLog(@"嘗試加鎖失敗");
    }
});

2016-08-19 11:42:54.433 ThreadLockControlDemo[1256:56857] 線程1
2016-08-19 11:42:55.434 ThreadLockControlDemo[1256:56861] 嘗試加鎖失敗

由上面的結(jié)果可得知,tryLock 并不會阻塞線程。[lock tryLock] 能加鎖返回 YES,不能加鎖返回 NO,然后都會執(zhí)行后續(xù)代碼。

如果將 [lock tryLock] 替換成

[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]

的話,則會返回 YES,輸出 “線程 2“,lockBeforeDate: 方法會在所指定 Date 之前嘗試加鎖,會阻塞線程,如果在指定時間之前都不能加鎖,則返回 NO,指定時間之前能加鎖,則返回 YES。

至于 _priv 和 name,我監(jiān)測了各個階段他們的值,_priv 一直都是 NULL。也不知道有什么用,name 是用來標(biāo)識用的,用來輸出 error log 時候作為 lock 的名稱。比如解鎖狀態(tài)再解鎖,會輸出一個 error log。

*** -[NSLock unlock]: lock (<NSLock: 0x7a4bdeb0> 'lockName') unlocked when not locked

如果是三個線程,那么一個線程在加鎖的時候,其余請求鎖的線程將形成一個等待隊列,按先進先出原則,這個結(jié)果可以通過修改線程優(yōu)先級進行測試得出。

NSConditionLock

@interface NSConditionLock : NSObject <NSLocking> {
@private
    void *_priv;
}
- (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;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end

NSConditionLock 和 NSLock 類似,都遵循 NSLocking 協(xié)議,方法都類似,只是多了一個 condition 屬性,以及每個操作都多了一個關(guān)于 condition 屬性的方法,例如 tryLock,tryLockWhenCondition:,NSConditionLock 可以稱為條件鎖,只有 condition 參數(shù)與初始化時候的 condition 相等,lock 才能正確進行加鎖操作。而 unlockWithCondition: 并不是當(dāng) Condition 符合條件時才解鎖,而是解鎖之后,修改 Condition 的值,這個結(jié)論可以從下面的例子中得出。

//主線程中
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];

//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lockWhenCondition:1];
    NSLog(@"線程1");
    sleep(2);
    [lock unlock];
});

//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);//以保證讓線程2的代碼后執(zhí)行
    if ([lock tryLockWhenCondition:0]) {
        NSLog(@"線程2");
        [lock unlockWithCondition:2];
        NSLog(@"線程2解鎖成功");
    } else {
        NSLog(@"線程2嘗試加鎖失敗");
    } });
//線程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(2);//以保證讓線程2的代碼后執(zhí)行
    if ([lock tryLockWhenCondition:2]) {
        NSLog(@"線程3");
        [lock unlock];
        NSLog(@"線程3解鎖成功");
    } else {
        NSLog(@"線程3嘗試加鎖失敗");
    } });
//線程4
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(3);//以保證讓線程2的代碼后執(zhí)行
    if ([lock tryLockWhenCondition:2]) {
        NSLog(@"線程4");
        [lock unlockWithCondition:1];
        NSLog(@"線程4解鎖成功");
    }else {
        NSLog(@"線程4嘗試加鎖失敗");
    } });

2016-08-19 13:51:15.353 ThreadLockControlDemo[1614:110697] 線程2
2016-08-19 13:51:15.354 ThreadLockControlDemo[1614:110697] 線程2解鎖成功
2016-08-19 13:51:16.353 ThreadLockControlDemo[1614:110689] 線程3
2016-08-19 13:51:16.353 ThreadLockControlDemo[1614:110689] 線程3解鎖成功
2016-08-19 13:51:17.354 ThreadLockControlDemo[1614:110884] 線程4
2016-08-19 13:51:17.355 ThreadLockControlDemo[1614:110884] 線程4解鎖成功
2016-08-19 13:51:17.355 ThreadLockControlDemo[1614:110884] 線程1

上面代碼先輸出了 ”線程 2“,因為線程 1 的加鎖條件不滿足,初始化時候的 condition 參數(shù)為 0,而加鎖條件是 condition 為 1,所以加鎖失敗。lockWhenCondition 與 lock 方法類似,加鎖失敗會阻塞線程,所以線程 1 會被阻塞著,而 tryLockWhenCondition 方法就算條件不滿足,也會返回 NO,不會阻塞當(dāng)前線程。

回到上面的代碼,線程 2 執(zhí)行了 [lock unlockWithCondition:2]; 所以 Condition 被修改成了 2。

而線程 3 的加鎖條件是 Condition 為 2, 所以線程 3 才能加鎖成功,線程 3 執(zhí)行了 [lock unlock]; 解鎖成功且不改變 Condition 值。

線程 4 的條件也是 2,所以也加鎖成功,解鎖時將 Condition 改成 1。這個時候線程 1 終于可以加鎖成功,解除了阻塞。

從上面可以得出,NSConditionLock 還可以實現(xiàn)任務(wù)之間的依賴。

NSRecursiveLock

@interface NSRecursiveLock : NSObject <NSLocking> {
@private
    void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end

NSRecursiveLock 是遞歸鎖,他和 NSLock 的區(qū)別在于,NSRecursiveLock 可以在一個線程中重復(fù)加鎖(反正單線程內(nèi)任務(wù)是按順序執(zhí)行的,不會出現(xiàn)資源競爭問題),NSRecursiveLock 會記錄上鎖和解鎖的次數(shù),當(dāng)二者平衡的時候,才會釋放鎖,其它線程才可以上鎖成功。

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    static void (^RecursiveBlock)(int);
    RecursiveBlock = ^(int value) {
        [lock lock];
        if (value > 0) {
            NSLog(@"value:%d", value);
            RecursiveBlock(value - 1);
        }
        [lock unlock];
    };
    RecursiveBlock(2);
});

2016-08-19 14:43:12.327 ThreadLockControlDemo[1878:145003] value:2
2016-08-19 14:43:12.327 ThreadLockControlDemo[1878:145003] value:1

如上面的示例,如果用 NSLock 的話,lock 先鎖上了,但未執(zhí)行解鎖的時候,就會進入遞歸的下一層,而再次請求上鎖,阻塞了該線程,線程被阻塞了,自然后面的解鎖代碼不會執(zhí)行,而形成了死鎖。而 NSRecursiveLock 遞歸鎖就是為了解決這個問題。

NSCondition

@interface NSCondition : NSObject <NSLocking> {
    @private
    void *_priv;
}
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end

NSCondition 的對象實際上作為一個鎖和一個線程檢查器,鎖上之后其它線程也能上鎖,而之后可以根據(jù)條件決定是否繼續(xù)運行線程,即線程是否要進入 waiting 狀態(tài),經(jīng)測試,NSCondition 并不會像上文的那些鎖一樣,先輪詢,而是直接進入 waiting 狀態(tài),當(dāng)其它線程中的該鎖執(zhí)行 signal 或者 broadcast 方法時,線程被喚醒,繼續(xù)運行之后的方法。

用法如下:

NSCondition *lock = [[NSCondition alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    while (!array.count) {
        [lock wait];
    }
    [array removeAllObjects];
    NSLog(@"array removeAllObjects");
    [lock unlock];
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);//以保證讓線程2的代碼后執(zhí)行
    [lock lock];
    [array addObject:@1];
    NSLog(@"array addObject:@1");
    [lock signal];
    [lock unlock];
});

2018-08-01 14:35:41.831727+0800 TEst[3825:269307] array addObject:@1
2018-08-01 14:35:41.832124+0800 TEst[3825:269305] array removeAllObjects

也就是使用 NSCondition 的模型為:

鎖定條件對象。

測試是否可以安全的履行接下來的任務(wù)。

如果布爾值是假的,調(diào)用條件對象的 wait 或 waitUntilDate: 方法來阻塞線程。 在從這些方法返回,則轉(zhuǎn)到步驟 2 重新測試你的布爾值。 (繼續(xù)等待信號和重新測試,直到可以安全的履行接下來的任務(wù)。waitUntilDate: 方法有個等待時間限制,指定的時間到了,則放回 NO,繼續(xù)運行接下來的任務(wù))

如果布爾值為真,執(zhí)行接下來的任務(wù)。

當(dāng)任務(wù)完成后,解鎖條件對象。

而步驟 3 說的等待的信號,既線程 2 執(zhí)行 [lock signal] 發(fā)送的信號。

其中 signal 和 broadcast 方法的區(qū)別在于,signal 只是一個信號量,只能喚醒一個等待的線程,想喚醒多個就得多次調(diào)用,而 broadcast 可以喚醒所有在等待的線程。如果沒有等待的線程,這兩個方法都沒有作用。

關(guān)于@synchronized,這兒比你想知道的還要多

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(self) {
        sleep(2);
        NSLog(@"線程1");
    }
    NSLog(@"線程1解鎖成功");
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    @synchronized(self) {
        NSLog(@"線程2");
    }
});

2016-08-19 16:42:21.752 ThreadLockControlDemo[2220:208291] 線程1
2016-08-19 16:42:21.752 ThreadLockControlDemo[2220:208291] 線程1解鎖成功
2016-08-19 16:42:21.752 ThreadLockControlDemo[2220:208278] 線程2

@synchronized(object) 指令使用的 object 為該鎖的唯一標(biāo)識,只有當(dāng)標(biāo)識相同時,才滿足互斥,所以如果線程 2 中的 @synchronized(self) 改為@synchronized(self.view),則線程2就不會被阻塞,@synchronized 指令實現(xiàn)鎖的優(yōu)點就是我們不需要在代碼中顯式的創(chuàng)建鎖對象,便可以實現(xiàn)鎖的機制,但作為一種預(yù)防措施,@synchronized 塊會隱式的添加一個異常處理例程來保護代碼,該處理例程會在異常拋出的時候自動的釋放互斥鎖。@synchronized 還有一個好處就是不用擔(dān)心忘記解鎖了。

如果在 @sychronized(object){} 內(nèi)部 object 被釋放或被設(shè)為 nil,從我做的測試的結(jié)果來看,的確沒有問題,但如果 object 一開始就是 nil,則失去了鎖的功能。不過雖然 nil 不行,但 @synchronized([NSNull null]) 是完全可以的。

dispatch_semaphore

dispatch_semaphore_create(long value); 
dispatch_semaphore_wait(dispatch_semaphore_t dsema, 
dispatch_time_t timeout); dispatch_semaphore_signal(dispatch_semaphore_t dsema);

dispatch_semaphore 是 GCD 用來同步的一種方式,與他相關(guān)的只有三個函數(shù),一個是創(chuàng)建信號量,一個是等待信號,一個是發(fā)送信號。

dispatch_semaphore_t signal = dispatch_semaphore_create(1);
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(signal, overTime);
    sleep(2);
    NSLog(@"線程1");
    dispatch_semaphore_signal(signal);
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    dispatch_semaphore_wait(signal, overTime);
    NSLog(@"線程2");
    dispatch_semaphore_signal(signal);
});

2018-08-01 14:53:00.657015+0800 TEst[4004:292557] 線程1
2018-08-01 14:53:00.657440+0800 TEst[4004:292559] 線程2

1、dispatch_semaphore 和 NSCondition 類似,都是一種基于信號的同步方式,但 NSCondition 信號只能發(fā)送,不能保存(如果沒有線程在等待,則發(fā)送的信號會失效)。而 dispatch_semaphore 能保存發(fā)送的信號。dispatch_semaphore 的核心是 dispatch_semaphore_t 類型的信號量。

2、dispatch_semaphore_create(1)方法可以創(chuàng)建一個 dispatch_semaphore_t 類型的信號量,設(shè)定信號量的初始值為 1。注意,這里的傳入的參數(shù)必須大于或等于 0,否則 dispatch_semaphore_create 會返回 NULL。

3、dispatch_semaphore_wait(signal, overTime); 方法會判斷 signal 的信號值是否大于 0。大于 0 不會阻塞線程,消耗掉一個信號,執(zhí)行后續(xù)任務(wù)。如果信號值為 0,該線程會和 NSCondition 一樣直接進入 waiting 狀態(tài),等待其他線程發(fā)送信號喚醒線程去執(zhí)行后續(xù)任務(wù),或者當(dāng) overTime 時限到了,也會執(zhí)行后續(xù)任務(wù)。

4、dispatch_semaphore_signal(signal);發(fā)送信號,如果沒有等待的線程接受信號,則使 signal 信號值加一(做到對信號的保存)。

從上面的實例代碼可以看到,一個 dispatch_semaphore_wait(signal, overTime); 方法會去對應(yīng)一個 dispatch_semaphore_signal(signal); 看起來像 NSLock 的 lock 和 unlock,其實可以這樣理解,區(qū)別只在于有信號量這個參數(shù),lock unlock 只能同一時間,一個線程訪問被保護的臨界區(qū),而如果 dispatch_semaphore 的信號量初始值為 x ,則可以有 x 個線程同時訪問被保護的臨界區(qū)。

OSSpinLock

objc typedef int32_t OSSpinLock;
bool OSSpinLockTry( volatile OSSpinLock *__lock );
void OSSpinLockLock( volatile OSSpinLock *__lock );
void OSSpinLockUnlock( volatile OSSpinLock *__lock );

OSSpinLock 是一種自旋鎖,也只有加鎖,解鎖,嘗試加鎖三個方法。和 NSLock 不同的是 NSLock 請求加鎖失敗的話,會先輪詢,但一秒過后便會使線程進入 waiting 狀態(tài),等待喚醒。而 OSSpinLock 會一直輪詢,等待時會消耗大量 CPU 資源,不適用于較長時間的任務(wù)。

__block OSSpinLock theLock = OS_SPINLOCK_INIT;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    OSSpinLockLock(&theLock);
    NSLog(@"線程1");
    sleep(10);
    OSSpinLockUnlock(&theLock);
    NSLog(@"線程1解鎖成功");
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    OSSpinLockLock(&theLock);
    NSLog(@"線程2");
    OSSpinLockUnlock(&theLock);
});

2016-08-19 20:25:13.526 ThreadLockControlDemo[2856:316247] 線程1
2016-08-19 20:25:23.528 ThreadLockControlDemo[2856:316247] 線程1解鎖成功
2016-08-19 20:25:23.529 ThreadLockControlDemo[2856:316260] 線程2

拿上面的輸出結(jié)果和上文 NSLock 的輸出結(jié)果做對比,會發(fā)現(xiàn) sleep(10) 的情況,OSSpinLock 中的“線程 2”并沒有和”線程 1解鎖成功“在一個時間輸出,而 NSLock 這里是同一時間輸出,而是有一點時間間隔,所以 OSSpinLock 一直在做著輪詢,而不是像 NSLock 一樣先輪詢,再 waiting 等喚醒。

pthread_mutex

int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * __restrict);

int pthread_mutex_lock(pthread_mutex_t *);

int pthread_mutex_trylock(pthread_mutex_t *);

int pthread_mutex_unlock(pthread_mutex_t *);

int pthread_mutex_destroy(pthread_mutex_t *);

int pthread_mutex_setprioceiling(pthread_mutex_t * __restrict, int, int * __restrict);

int pthread_mutex_getprioceiling(const pthread_mutex_t * __restrict, int * __restrict);

pthread pthread_mutex 是 C 語言下多線程加互斥鎖的方式,那來段 C 風(fēng)格的示例代碼,需要 #import <pthread.h>

static pthread_mutex_t theLock;

- (void)example5 {
    pthread_mutex_init(&theLock, NULL);
    
    pthread_t thread;
    pthread_create(&thread, NULL, threadMethord1, NULL);
    
    pthread_t thread2;
    pthread_create(&thread2, NULL, threadMethord2, NULL);
}

void *threadMethord1() {
    pthread_mutex_lock(&theLock);
    printf("線程1\n");
    sleep(2);
    pthread_mutex_unlock(&theLock);
    printf("線程1解鎖成功\n");
    return 0;
    
}

void *threadMethord2() {
    sleep(1);
    pthread_mutex_lock(&theLock);
    printf("線程2\n");
    pthread_mutex_unlock(&theLock);
    return 0;
}

線程1
線程1解鎖成功
線程2
int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * __restrict);

首先是第一個方法,這是初始化一個鎖,__restrict 為互斥鎖的類型,傳 NULL 為默認(rèn)類型,一共有 4 類型。

PTHREAD_MUTEX_NORMAL 缺省類型,也就是普通鎖。
當(dāng)一個線程加鎖以后,其余請求鎖的線程將形成一個等待隊列,并在解鎖后先進先出原則獲得鎖。 

PTHREAD_MUTEX_ERRORCHECK 檢錯鎖,如果同一個線程請求同一個鎖,則返回 EDEADLK,
否則與普通鎖類型動作相同。這樣就保證當(dāng)不允許多次加鎖時不會出現(xiàn)嵌套情況下的死鎖。 

PTHREAD_MUTEX_RECURSIVE 遞歸鎖,
允許同一個線程對同一個鎖成功獲得多次,并通過多次 unlock 解鎖。 

PTHREAD_MUTEX_DEFAULT 適應(yīng)鎖,動作最簡單的鎖類型,
僅等待解鎖后重新競爭,沒有等待隊列。

通過 pthread_mutexattr_t 來設(shè)置鎖的類型,如下面代碼就設(shè)置鎖為遞歸鎖。實現(xiàn)和 NSRecursiveLock 類似的效果。如下面的示例代碼:

- (void)example5 {
    pthread_mutex_init(&theLock, NULL);
    
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&theLock, &attr);
    pthread_mutexattr_destroy(&attr);
    
    pthread_t thread;
    pthread_create(&thread, NULL, threadMethord, 5);
}

void *threadMethord(int value) {
    pthread_mutex_lock(&theLock);
    if (value > 0) {
        printf("Value:%i\n", value);
        sleep(1);
        threadMethord(value - 1);
    }
    pthread_mutex_unlock(&theLock);
    return 0;
}

Value:5
Value:4
Value:3
Value:2
Value:1

回到 pthread_mutex,鎖初始化完畢,就要上鎖解鎖了

pthread_mutex_lock(&theLock);
pthread_mutex_unlock(&theLock);

和 NSLock 的 lock unlock 用法一致,但還注意到有一個 pthread_mutex_trylock 方法,pthread_mutex_trylock 和 tryLock 的區(qū)別在于,tryLock 返回的是 YES 和 NO,pthread_mutex_trylock 加鎖成功返回的是 0,失敗返回的是錯誤提示碼。

pthread_mutex_destroy 為釋放鎖資源。

性能

1.png

當(dāng)然只是加鎖立馬解鎖的時間消耗,并沒有計算競爭時候的時間消耗??梢钥闯?OSSpinLock 性能最高,但它已經(jīng)不再安全,如果一個低優(yōu)先級的線程獲得鎖并訪問共享資源,這時一個高優(yōu)先級的線程也嘗試獲得這個鎖,由于它會處于輪詢的忙等狀態(tài)從而占用大量 CPU。此時低優(yōu)先級線程無法與高優(yōu)先級線程爭奪 CPU 時間,從而導(dǎo)致任務(wù)遲遲完不成、無法釋放 lock。

圖中的 pthread_mutex(recursive) 指的是 pthread_mutex 設(shè)置為遞歸鎖的情況。

從圖中可以知道 @synchronized 的效率最低,不過它的確用起來最方便,所以如果沒什么性能瓶頸的話,使用它也不錯。

總結(jié)
雖然這些鎖看起來很復(fù)雜,但最終都是加鎖,等待,解鎖。

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

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

  • 鎖是一種同步機制,用于多線程環(huán)境中對資源訪問的限制iOS中常見鎖的性能對比圖(摘自:ibireme): iOS鎖的...
    LiLS閱讀 1,642評論 0 6
  • 本文節(jié)選自成長手冊 文章推薦和參考深入理解 iOS 開發(fā)中的鎖pthread的各種同步機制 多線程編程被普遍認(rèn)為復(fù)...
    百草紀(jì)閱讀 2,920評論 1 9
  • 線程安全是怎么產(chǎn)生的 常見比如線程內(nèi)操作了一個線程外的非線程安全變量,這個時候一定要考慮線程安全和同步。 - (v...
    幽城88閱讀 777評論 0 0
  • 在平時的開發(fā)中經(jīng)常使用到多線程,在使用多線程的過程中,難免會遇到資源競爭的問題,那我們怎么來避免出現(xiàn)這種問題那? ...
    IAMCJ閱讀 3,354評論 2 25
  • 1.今天學(xué)了什么新內(nèi)容? 吸氣:換(換氣) 偷(偷氣) 搶(搶氣) 頓(頓氣) 呼氣:順(順帶) 推(平推) 頂(...
    溫大P39李佳琪閱讀 423評論 1 1

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