iOS 之 線程鎖整理

前言

最開始我想把線程和線程鎖放在一起整理出一篇文章,結(jié)果整理了線程發(fā)現(xiàn)有點長,于是便把線程鎖單獨拿出來了。感興趣的小伙伴也可以去看下線程的生命周期,NSThread、GCD、NSOperation的使用與總結(jié),因為兩篇文章原本是想放在一起的。


正文

一、鎖的一些概念和性能對比

1.1 為什么要使用鎖(線程安全)

線程安全是指,當(dāng)一個線程訪問數(shù)據(jù)的時候,其他的線程不能對其進行訪問,直到該線程訪問完畢。簡單來講就是在同一時刻,對同一個數(shù)據(jù)操作的線程只有一個。只有確保了這樣,才能使數(shù)據(jù)不會被其他線程影響。而線程不安全,則是在同一時刻可以有多個線程對該數(shù)據(jù)進行訪問,從而得不到預(yù)期的結(jié)果。

舉例來說:現(xiàn)在僅剩余一張火車票,每一個購票請求都是一個線程,那么同一時刻有多個線程同時請求出票,那么剩余的這一張票將會同時出票多次,這明顯是不合理的,所以鎖的出現(xiàn),就是為了確保線程安全問題。

1.2 鎖的一些概念
  • 臨界資源: 多個線程共享各種資源,然而有很多資源一次只能供一個線程使用。一次僅允許一個線程使用的資源稱為臨界資源。

  • 臨界區(qū):訪問臨界資源的代碼區(qū)。

  • 死鎖:指兩個或兩個以上的線程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象(都要等待對方完成某個操作才能進行下一步),若無外力作用,它們都將無法推進下去,這時就會發(fā)生死鎖。

  • 上下文切換(Context Switch):在操作系統(tǒng)中,CPU切換到另一個進程需要保存當(dāng)前進程的狀態(tài)并恢復(fù)另一個進程的狀態(tài):當(dāng)前運行任務(wù)轉(zhuǎn)為就緒(或者掛起、刪除)狀態(tài),另一個被選定的就緒任務(wù)成為當(dāng)前任務(wù)。上下文切換包括保存當(dāng)前任務(wù)的運行環(huán)境,恢復(fù)將要運行任務(wù)的運行環(huán)境。

  • 輪詢(Polling):一種CPU決策如何提供周邊設(shè)備服務(wù)的方式,又稱“程控輸入輸出”(Programmed I/O)。輪詢法的概念是,由CPU定時發(fā)出詢問,依序詢問每一個周邊設(shè)備是否需要其服務(wù),有即給予服務(wù),服務(wù)結(jié)束后再問下一個周邊,接著不斷周而復(fù)始。

  • 原子屬性:

    • atomic:原子屬性,設(shè)置成員變量的@property屬性時,默認(rèn)為atomic,提供多線程安全。atomic是為setter方法加鎖,將屬性以atomic的形式來聲明,該屬性變量就能支持互斥鎖了。而這種機制是耗費系統(tǒng)資源的。

    • nonatomic:非原子屬性,不會為setter方法加鎖,聲明為該屬性的變量,客戶端應(yīng)盡量避免多線程爭奪同一資源。

  • 自旋鎖:線程反復(fù)檢查鎖變量是否可用(類似于while(鎖沒解開)),因此是一種忙等狀態(tài),將一直占用CPU資源。

  • 讀寫鎖:讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對于自旋鎖而言,能提高并發(fā)性,因為在多處理器系統(tǒng)中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數(shù)為實際的邏輯CPU數(shù)。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數(shù)相關(guān)),但不能同時既有讀者又有寫者。

  • 互斥鎖:在被訪問的資源被鎖時,會讓當(dāng)前線程進入休眠狀態(tài),不再占用CPU資源,一旦被訪問的資源被解鎖,則等待資源的線程會被喚醒。

  • 條件鎖:在一定條件下,讓其等待休眠,并放開鎖,等接收到信號或者廣播,會從新喚起線程,并重新加鎖。

  • 遞歸鎖:同一個線程可以多次加鎖,不會造成死鎖,不同線程來訪問這段代碼時,發(fā)現(xiàn)有鎖要等待所有鎖解開之后才可以繼續(xù)往下走。

  • 信號量(Semaphore):有時被稱為信號燈,是在多線程環(huán)境下使用的一種設(shè)施,是可以用來保證兩個或多個關(guān)鍵代碼段不被并發(fā)調(diào)用。在進入一個關(guān)鍵代碼段之前,線程必須獲取一個信號量;一旦該關(guān)鍵代碼段完成了,那么該線程必須釋放信號量。其它想進入該關(guān)鍵代碼段的線程必須等待直到第一個線程釋放信號量。

1.3 性能對比
鎖的性能比較.png
//10000000
OSSpinLock:                 112.38 ms
dispatch_semaphore:         160.37 ms
os_unfair_lock:             208.87 ms
pthread_mutex:              302.07 ms
NSCondition:                320.11 ms
NSLock:                     331.80 ms
pthread_rwlock:             360.81 ms
pthread_mutex(recursive):   512.17 ms
NSRecursiveLock:            667.55 ms
NSConditionLock:            999.91 ms
@synchronized:             1654.92 ms
//1000
OSSpinLock:                   0.02 ms
dispatch_semaphore:           0.03 ms
os_unfair_lock:               0.04 ms
pthread_mutex:                0.06 ms
NSLock:                       0.06 ms
pthread_rwlock:               0.07 ms
NSCondition:                  0.07 ms
pthread_mutex(recursive):     0.09 ms
NSRecursiveLock:              0.12 ms
NSConditionLock:              0.18 ms
@synchronized:                0.33 ms

二、鎖的使用(種類)

上鎖有兩種方式trylocklock:當(dāng)前線程鎖失敗,也可以繼續(xù)其它任務(wù),用 trylock 合適;當(dāng)前線程只有鎖成功后,才會做一些有意義的工作,那就 lock,沒必要輪詢 trylock。
注:以下大部分鎖都會提供trylock接口,不再作解釋。

2.1 OSSpinLock (自旋鎖)
  • OSSpinLock 是一種自旋鎖,也只有加鎖,解鎖,嘗試加鎖三個方法。
  • OSSpinLock 會一直輪詢,等待時會消耗大量 CPU 資源,不適用于較長時間的任務(wù)。
  • OSSpinLock 有潛在的優(yōu)先級反轉(zhuǎn)問題,iOS10.0以后棄用了這種鎖機制,使用os_unfair_lock。
需要導(dǎo)入頭文件
#import <libkern/OSAtomic.h>
// 初始化
OSSpinLock spinLock = OS_SPINLOCK_INIT;
// 加鎖
OSSpinLockLock(&spinLock);
// 解鎖
OSSpinLockUnlock(&spinLock);
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
OSSpinLockTry(&spinLock)

以GCD為例,代碼以及執(zhí)行結(jié)果如下:

2.1.1 OSSpinLockLock
// OSSpinLockLock
- (void)UseOSSpinLock{
    __block OSSpinLock spinLock = OS_SPINLOCK_INIT;
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        OSSpinLockLock(&spinLock);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程1  第 %d 次",i);
        }
        OSSpinLockUnlock(&spinLock);
    });

    dispatch_async(queue, ^{
        OSSpinLockLock(&spinLock);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程2  第 %d 次",i);
        }
        OSSpinLockUnlock(&spinLock);
    });

    dispatch_async(queue, ^{
        OSSpinLockLock(&spinLock);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程3  第 %d 次",i);
        }
        OSSpinLockUnlock(&spinLock);
    });
}
OSSpinLockLock

我們可以看到,我們用的并發(fā)異步線程,但是加鎖之后,執(zhí)行結(jié)果并沒有并發(fā)異步執(zhí)行。

2.1.2 OSSpinLockTry
// OSSpinLockTry
- (void)UseOSSpinLock{
    __block OSSpinLock spinLock = OS_SPINLOCK_INIT;
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        OSSpinLockTry(&spinLock);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程4  第 %d 次",i);
        }
        OSSpinLockUnlock(&spinLock);
    });

    dispatch_async(queue, ^{
        OSSpinLockTry(&spinLock);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程5  第 %d 次",i);
        }
        OSSpinLockUnlock(&spinLock);
    });

    dispatch_async(queue, ^{
        OSSpinLockTry(&spinLock);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程6  第 %d 次",i);
        }
        OSSpinLockUnlock(&spinLock);
    });
}
OSSpinLockTry-1

執(zhí)行結(jié)果可以看出來,OSSpinLockTry并沒有阻塞線程。也符合上面所說:當(dāng)前線程鎖失敗,也可以繼續(xù)其它任務(wù)。但是這只是測試一下,項目中不要這么寫,因為這樣沒有意義,可以如下:

//OSSpinLockTry
- (void)UseOSSpinLock{
    __block OSSpinLock spinLock = OS_SPINLOCK_INIT;
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        if(OSSpinLockTry(&spinLock)){
            for(int i = 0; i < 3; i++){
                sleep(1);
                NSLog(@"線程7  第 %d 次",i);
            }
            OSSpinLockUnlock(&spinLock);
        }else{
            NSLog(@"鎖7失敗,執(zhí)行一些其他事情");
        }
    });

    dispatch_async(queue, ^{
        if(OSSpinLockTry(&spinLock)){
            for(int i = 0; i < 3; i++){
                sleep(1);
                NSLog(@"線程8  第 %d 次",i);
            }
            OSSpinLockUnlock(&spinLock);
        }else{
            NSLog(@"鎖8失敗,執(zhí)行一些其他事情");
        }
    });

    dispatch_async(queue, ^{
        if(OSSpinLockTry(&spinLock)){
            for(int i = 0; i < 3; i++){
                sleep(1);
                NSLog(@"線程9  第 %d 次",i);
            }
            OSSpinLockUnlock(&spinLock);
        }else{
            NSLog(@"鎖9失敗,執(zhí)行一些其他事情");
        }
    });
    
}
OSSpinLockTry-2

執(zhí)行結(jié)果可以看出,加鎖失敗后執(zhí)行另一部分代碼,并沒有自旋去等待加鎖,執(zhí)行后其他鎖釋放也不會再次加鎖,所以用的時候要考慮場景。

2.2 os_unfair_lock(互斥鎖)

os_unfair_lock網(wǎng)上很多文章,有說它是自旋鎖的,但是官方文檔中有一段是這樣的:

This is a replacement for the deprecated OSSpinLock. This function doesn't spin on contention, but instead waits in the kernel to be awoken by an unlock.

自我理解為:“這是對已棄用的osspinlock的替換。這個函數(shù)不會在爭用時自旋,而是在內(nèi)核中等待解鎖來喚醒?!彼?,它應(yīng)該是互斥鎖,并不是自旋鎖。

需要導(dǎo)入頭文件
#import <os/lock.h>
// 初始化
 os_unfair_lock unfair_lock = OS_UNFAIR_LOCK_INIT;
// 加鎖
os_unfair_lock_lock(&unfair_lock);
// 解鎖
os_unfair_lock_unlock(&unfair_lock);
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
os_unfair_lock_trylock(&unfair_lock);
/*
注:解決不同優(yōu)先級的線程申請鎖的時候不會發(fā)生優(yōu)先級反轉(zhuǎn)問題.
不過相對于 OSSpinLock , os_unfair_lock性能方面減弱了許多.
*/

使用方法上同,不做示范。

2.3 dispatch_semaphore (信號量)

信號量,是持有計數(shù)的信號。個人覺得有點類似于引用計數(shù):create時定義最大線程數(shù),使用時wait進行計數(shù)-1,結(jié)束時signal進行計數(shù)+1,當(dāng)計數(shù)大于零時可執(zhí)行,等于零時阻塞線程進行等待執(zhí)行。
信號量的作用,個人覺得有以下幾點:

  • 控制線程數(shù)量(最大并發(fā)數(shù)),我們知道NSOperationQueue我們可以設(shè)置maxConcurrentOperationCount來控制最大并發(fā)數(shù),但是GCD的話,并沒有一個屬性可以控制最大并發(fā)數(shù),所以我們可以用信號量來控制GCD的最大并發(fā)數(shù)。
  • 線程安全(鎖),信號量的本質(zhì)還是控制子線程并發(fā)數(shù)量,而我們可以設(shè)置最大并發(fā)量為1,然后在臨界區(qū)(多條線程都會訪問的代碼區(qū))進行信號量控制,保證同一時刻只有一條線程執(zhí)行此段代碼,從而保證線程安全。
  • 線程同步,會在后面代碼說明。
  • 阻塞線程,會在后面和同步代碼一起說明。
// 初始化
dispatch_semaphore_t semaphore_t = dispatch_semaphore_create(1);
// 加鎖
dispatch_semaphore_wait(semaphore_t,DISPATCH_TIME_FOREVER);
// 解鎖
dispatch_semaphore_signal(semaphore_t);
2.3.1 最大并發(fā)數(shù)

不多說,直接上代碼:

//2.dispatch_semaphore
- (void)UseSemaphore{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程1 第%d次 線程:%@",i,[NSThread currentThread]);
        }
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程2 第%d次 線程:%@",i,[NSThread currentThread]);
        }
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程3 第%d次 線程:%@",i,[NSThread currentThread]);
        }
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        for(int i = 0; i < 3; i++){
            sleep(1);
            NSLog(@"線程4 第%d次 線程:%@",i,[NSThread currentThread]);
        }
        dispatch_semaphore_signal(semaphore);
    });
}

最大并發(fā)數(shù)

通過結(jié)果我們可以看到,一共是四個并發(fā)異步線程,但是由于設(shè)置信號量,間接控制了最大并發(fā)數(shù)。值得注意的是:最大并發(fā)數(shù),是指執(zhí)行任務(wù)的線程最多是兩個(信號量設(shè)置的是兩個),但是,處于回收狀態(tài)的線程不算此列.也就是說,執(zhí)行任務(wù)的時候不只有兩個線程,還有處于回收狀態(tài)的線程,所以子線程個數(shù)不為2;

2.3.2 線程安全

線程不安全時:

- (void)UseSemaphoreLock{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    self.number = 50;
    __weak typeof(self) weakSelf = self;
    dispatch_async(queue, ^{
        for(int i = 0; i < 3; i++){
            [weakSelf changeNumber];
        }
    });
    dispatch_async(queue, ^{
        for(int i = 0; i < 3; i++){
            [weakSelf changeNumber];
        }
    });
}

- (void)changeNumber{
    _number = _number - 1;
    sleep(1);
    NSLog(@"number == %ld",_number);
}
線程不安全時

我們可以看到,如果異步線程,同時更改同意資源時,那么可能出現(xiàn)數(shù)據(jù)混亂。所以我們可以用信號量加鎖,保證線程安全:

- (void)UseSemaphoreLock{
    self.semaphore1 = dispatch_semaphore_create(1);
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    self.number = 50;
    __weak typeof(self) weakSelf = self;
    dispatch_async(queue, ^{
        for(int i = 0; i < 3; i++){
            [weakSelf changeNumber];
        }
    });
    dispatch_async(queue, ^{
        for(int i = 0; i < 3; i++){
            [weakSelf changeNumber];
        }
    });
}

- (void)changeNumber{
    //相當(dāng)于加鎖
    dispatch_semaphore_wait(_semaphore1, DISPATCH_TIME_FOREVER);
    _number = _number - 1;
    sleep(1);
    NSLog(@"number == %ld",_number);
    //相當(dāng)于解鎖
    dispatch_semaphore_signal(_semaphore1);
}

線程安全時

如上,用信號量可以處理線程安全問題。(當(dāng)然我們也可以把wait、signal加到異步線程當(dāng)中,但是覺得那么做的話,實際上還是控制了最大并發(fā)數(shù),并不是解決線程安全。)

2.3.3 線程同步
//semaphore實現(xiàn)線程同步
- (void)semaphoreSync {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    __block int number = 0;
    dispatch_async(queue, ^{
        // 追加任務(wù)1
        [NSThread sleepForTimeInterval:1];              // 模擬耗時操作
        NSLog(@"任務(wù)1 %@",[NSThread currentThread]);      // 打印當(dāng)前線程
        number = 100;
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore---end1,number = %d",number);
    dispatch_async(queue, ^{
        // 追加任務(wù)2
        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
        NSLog(@"任務(wù)2 %@",[NSThread currentThread]);      // 打印當(dāng)前線程
        
        number = 50;
        
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore---end2,number = %d",number);
    dispatch_async(queue, ^{
        // 追加任務(wù)3
        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
        NSLog(@"任務(wù)3 %@",[NSThread currentThread]);      // 打印當(dāng)前線程
        number = 10;
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore---end3,number = %d",number);
}

同步

可以看到,雖然我們建立的并行異步線程,但是執(zhí)行結(jié)果卻是同步執(zhí)行。原因如下:在主線程程中設(shè)置信號量為0dispatch_semaphore_create(0);當(dāng)執(zhí)行到追加任務(wù)1的子線程時,進入子線程,主線程繼續(xù)執(zhí)行dispatch_semaphore_wait,但是此時信號量為0,dispatch_semaphore_wait在此處阻塞主線程進入等待狀態(tài),直到任務(wù)1的子線程執(zhí)行dispatch_semaphore_signal使信號量+1,此時主線程中處于等待的dispatch_semaphore_wait可以使信號量-1,于是停止阻塞線程并繼續(xù)向下執(zhí)行。(利用阻塞線程實現(xiàn)線程同步)

2.4 pthread_mutex(互斥鎖)
  • pthread_mutex 是 C 語言下多線程加互斥鎖的方式。
  • 被這個鎖保護的臨界區(qū)就只允許一個線程進入,其它線程如果沒有獲得鎖權(quán)限,那就只能在外面等著。
需要導(dǎo)入頭文件
#import <pthread/pthread.h>
//聲明鎖
pthread_mutex_t mutex_t;
// 初始化(兩種)
1.普通初始化
pthread_mutex_init(&mutex_t, NULL); 
2.宏初始化
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
// 加鎖
pthread_mutex_lock(&mutex_t);
// 解鎖
pthread_mutex_unlock(&mutex_t);
// 嘗試加鎖,可以加鎖時返回的是 0,否則返回一個錯誤
pthread_mutex_trylock(& mutex_t)
// 釋放鎖
pthread_mutex_destroy(&_lock)
  • 鎖類型:
pthread_mutex_init(&mutex_t, NULL);
初始化鎖 NULL等同于PTHREAD_MUTEX_DEFAULT

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

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

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

PTHREAD_MUTEX_DEFAULT 適應(yīng)鎖,動作最簡單的鎖類型,僅等待解鎖后重新競爭,沒有等待隊列
2.5 NSCondition(條件鎖、對象鎖)
// 初始化
NSCondition *_condition= [[NSCondition alloc]init];
// 加鎖
[_condition lock];
// 解鎖
[_condition unlock];
/*
其他功能接口
wait 進入等待狀態(tài)
waitUntilDate:讓一個線程等待一定的時間
signal 喚醒一個等待的線程
broadcast 喚醒所有等待的線程
注: 所測時間波動太大, 有時候會快于 NSLock, 我取得中間值.
*/
2.6 NSLock(互斥鎖、對象鎖)
// 初始化
NSLock *_lock = [[NSLock alloc]init];
// 加鎖
[_lock lock];
// 解鎖
[_lock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_lock tryLock];
2.7 pthread_rwlock(讀寫鎖)

讀寫鎖又稱共享-互斥鎖:

//加讀鎖
pthread_rwlock_rdlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
//加寫鎖
pthread_rwlock_wrlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
2.8 pthread_mutex(recursive)(遞歸鎖)
// 初始化
pthread_mutex_t mutex_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); //初始化attr并且給它賦予默認(rèn)pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //設(shè)置鎖類型,這邊是設(shè)置為遞歸鎖
pthread_mutex_init(&mutex_t, &attr);
pthread_mutexattr_destroy(&attr); //銷毀一個屬性對象,在重新進行初始化之前該結(jié)構(gòu)不能重新使用
// 加鎖
pthread_mutex_lock(&mutex_t);
// 解鎖
pthread_mutex_unlock(&mutex_t);
/*
注: 遞歸鎖可以被同一線程多次請求,而不會引起死鎖。
即在同一線程中在未解鎖之前還可以上鎖, 執(zhí)行鎖中的代碼。
這主要是用在循環(huán)或遞歸操作中。
*/
2.9 NSRecursiveLock(遞歸鎖、對象鎖)
// 初始化
NSRecursiveLock *_recursiveLock = [[NSRecursiveLock alloc]init];
// 加鎖
[_recursiveLock lock];
// 解鎖
[_recursiveLock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_recursiveLock tryLock];
/*
注: 遞歸鎖可以被同一線程多次請求,而不會引起死鎖。
即在同一線程中在未解鎖之前還可以上鎖, 執(zhí)行鎖中的代碼。
這主要是用在循環(huán)或遞歸操作中。
- (BOOL)lockBeforeDate:(NSDate *)limit;//觸發(fā)鎖 在等待時間之內(nèi)
*/
2.10 NSConditionLock(條件鎖、對象鎖)
// 初始化
NSConditionLock *_conditionLock = [[NSConditionLock alloc]init];
// 加鎖
[_conditionLock lock];
// 解鎖
[_conditionLock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_conditionLock tryLock];
/*
其他功能接口
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER; //初始化傳入條件
- (void)lockWhenCondition:(NSInteger)condition;//條件成立觸發(fā)鎖
- (BOOL)tryLockWhenCondition:(NSInteger)condition;//嘗試條件成立觸發(fā)鎖
- (void)unlockWithCondition:(NSInteger)condition;//條件成立解鎖
- (BOOL)lockBeforeDate:(NSDate *)limit;//觸發(fā)鎖 在等待時間之內(nèi)
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//觸發(fā)鎖 條件成立 并且在等待時間之內(nèi)
*/
2.11 @synchronized()遞歸鎖、互斥鎖
// 初始化
@synchronized(鎖對象){
}
底層封裝的pthread_mutex的PTHREAD_MUTEX_RECURSIVE 模式,
鎖對象來表示是否為同一把鎖
最后編輯于
?著作權(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ù)。

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