iOS 線程鎖的使用

一、線程鎖相關(guān)概念

線程鎖:我們?cè)谑褂枚嗑€程的時(shí)候多個(gè)線程可能會(huì)訪問同一塊資源,這樣就很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全等問題,這時(shí)候就需要保證同一個(gè)時(shí)間只允許有限個(gè)線程訪問這一塊資源,所有就需要線程鎖,鎖也分為自旋鎖,互斥鎖,信號(hào)量等等。比如一個(gè)線程 A 進(jìn)入需要保護(hù)代碼之前添加簡(jiǎn)單的互斥鎖,另一個(gè)線程 B 就無法訪問,只有等待前一個(gè)線程 A 執(zhí)行完被保護(hù)的代碼后解鎖,B 線程才能訪問被保護(hù)代碼。

臨界區(qū):指的是一塊對(duì)公共資源進(jìn)行訪問的代碼,并非一種機(jī)制或是算法。

時(shí)間片(timeslice):分時(shí)操作系統(tǒng)分配給每個(gè)正在運(yùn)行的進(jìn)程微觀上的一段CPU時(shí)間(在搶占內(nèi)核中是:從進(jìn)程開始運(yùn)行直到被搶占的時(shí)間)。現(xiàn)代操作系統(tǒng)(如:Windows、Linux、Mac OS X等)允許同時(shí)運(yùn)行多個(gè)進(jìn)程 —— 例如,你可以在打開音樂播放器聽音樂的同時(shí)用瀏覽器瀏覽網(wǎng)頁并下載文件。事實(shí)上,由于一臺(tái)計(jì)算機(jī)通常只有一個(gè)CPU,所以永遠(yuǎn)不可能真正地同時(shí)運(yùn)行多個(gè)任務(wù)。這些進(jìn)程“看起來像”同時(shí)運(yùn)行的,實(shí)則是輪番穿插地運(yùn)行,由于時(shí)間片通常很短(在Linux上為5ms-800ms),用戶不會(huì)感覺到。

自旋鎖:是用于多線程同步的一種鎖,線程反復(fù)檢查鎖變量是否可用。由于線程在這一過程中保持執(zhí)行,因此是一種忙等(busy-wait)狀態(tài)。一旦獲取了自旋鎖,線程會(huì)一直保持該鎖,直至顯式釋放自旋鎖。 自旋鎖避免了進(jìn)程上下文的調(diào)度開銷,因此對(duì)于線程只會(huì)阻塞很短時(shí)間的場(chǎng)合是有效的,如果臨界區(qū)的執(zhí)行時(shí)間過長(zhǎng),使用自旋鎖不是個(gè)好主意。

互斥鎖(Mutex):是一種用于多線程編程中,防止兩條線程同時(shí)對(duì)同一公共資源(比如全局變量)進(jìn)行讀寫的機(jī)制。該目的通過將代碼切片成一個(gè)一個(gè)的臨界區(qū)而達(dá)成。

讀寫鎖:是計(jì)算機(jī)程序的并發(fā)控制的一種同步機(jī)制,也稱“共享-互斥鎖”、多讀者-單寫者鎖) 用于解決多線程對(duì)公共資源讀寫問題。讀操作可并發(fā)重入,寫操作是互斥的。 讀寫鎖通常用互斥鎖、條件變量、信號(hào)量實(shí)現(xiàn)。

信號(hào)量(semaphore):是一種更高級(jí)的同步機(jī)制,互斥鎖可以說是semaphore在僅取值0/1時(shí)的特例。信號(hào)量可以有更多的取值空間,用來實(shí)現(xiàn)更加復(fù)雜的同步,而不單單是線程間互斥。信號(hào)量的值為零時(shí),會(huì)使線程進(jìn)入睡眠狀態(tài),主動(dòng)讓出時(shí)間片,主動(dòng)讓出時(shí)間片并不總是代表效率高。讓出時(shí)間片會(huì)導(dǎo)致操作系統(tǒng)切換到另一個(gè)線程,這種上下文切換通常需要 10 微秒左右,而且至少需要兩次切換。如果等待時(shí)間很短,比如只有幾個(gè)微秒,忙等就比線程睡眠更高效。

條件鎖:就是條件變量,當(dāng)進(jìn)程的某些資源要求不滿足時(shí)就進(jìn)入休眠,也就是鎖住了。當(dāng)資源被分配到了,條件鎖打開,進(jìn)程繼續(xù)運(yùn)行。

二、iOS中的十一種鎖

1.OSSpinLock(自旋鎖)
2.os_unfair_lock(iOS 10以后,蘋果提供用來代替OSSpinLock)
3.dispatch_semaphore (信號(hào)量)
4.pthread_mutex(互斥鎖)
5.pthread_mutex(recursive) (遞歸鎖)
6.NSLock(互斥鎖)
7.NSCondition(條件鎖)
8.NSRecursiveLock(遞歸鎖)
9.NSConditionLock(條件鎖)
10.@synchronized(條件鎖)
11.pthread_rwlock(讀寫鎖)


1.OSSpinLock(自旋鎖)

測(cè)試中效率最高的鎖, 不過經(jīng)YYKit作者確認(rèn), OSSpinLock已經(jīng)不再線程安全,OSSpinLock有潛在的優(yōu)先級(jí)反轉(zhuǎn)問題.不再安全的 OSSpinLock在 iOS 10/macOS 10.12 發(fā)布時(shí),蘋果提供了新的 os_unfair_lock 作為 OSSpinLock 的替代,并且將 OSSpinLock 標(biāo)記為了 Deprecated。

OS_SPINLOCK_INIT: 默認(rèn)值為 0,在 locked 狀態(tài)時(shí)就會(huì)大于 0,unlocked狀態(tài)下為 0
OSSpinLockLock(&oslock):上鎖,參數(shù)為 OSSpinLock 地址
OSSpinLockUnlock(&oslock):解鎖,參數(shù)為 OSSpinLock 地址
OSSpinLockTry(&oslock):嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO

OSSpinLockLock測(cè)試代碼

#import <libkern/OSAtomic.h>

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //設(shè)置票數(shù)
    self.totalCount = 5;
    spinLock = OS_SPINLOCK_INIT;
    dispatch_queue_t queue = dispatch_queue_create("saleTicket", DISPATCH_QUEUE_CONCURRENT);
    //售票線程1
    dispatch_async(queue, ^{
        [NSThread currentThread].name = @"售票線程A";
        [self saleTicket];
    });
    NSLog(@"-----------------------");
    //售票線程2
    dispatch_async(queue, ^{
        [NSThread currentThread].name = @"售票線程B";
        [self saleTicket];
    });

}
-(void)saleTicket{
    while (1) {
//        [NSThread sleepForTimeInterval:1];
        NSLog(@"%@準(zhǔn)備上鎖",[NSThread currentThread].name);
        OSSpinLockLock(&spinLock);
        NSLog(@"%@已上鎖",[NSThread currentThread].name);
        if (self.totalCount > 0) {
            self.totalCount --;
            NSLog(@"%@賣出去了一張票,還剩下%zd張票", [NSThread currentThread].name,self.totalCount);
            OSSpinLockUnlock(&spinLock);
            NSLog(@"%@解鎖",[NSThread currentThread].name);
        }else{
            NSLog(@"%@:票已售完",[NSThread currentThread].name);
            OSSpinLockUnlock(&spinLock);
            NSLog(@"%@解鎖",[NSThread currentThread].name);
            break;
        }
    }
}

輸出結(jié)果(每次執(zhí)行結(jié)果不相同)

11:32:06.352257+0800 threadTest[2739:696809] 售票線程A準(zhǔn)備上鎖
11:32:06.352282+0800 threadTest[2739:696809] 售票線程A已上鎖
11:32:06.352614+0800 threadTest[2739:696809] 售票線程A賣出去了一張票,還剩下2張票
11:32:06.353249+0800 threadTest[2739:696809] 售票線程A解鎖
11:32:06.353371+0800 threadTest[2739:696809] 售票線程A準(zhǔn)備上鎖
11:32:06.352793+0800 threadTest[2739:696981] 售票線程B準(zhǔn)備上鎖
11:32:06.353399+0800 threadTest[2739:696809] 售票線程A已上鎖
11:32:06.353429+0800 threadTest[2739:696809] 售票線程A賣出去了一張票,還剩下1張票
11:32:06.353464+0800 threadTest[2739:696809] 售票線程A解鎖
11:32:06.353496+0800 threadTest[2739:696809] 售票線程A準(zhǔn)備上鎖
11:32:06.353656+0800 threadTest[2739:696981] 售票線程B已上鎖
11:32:06.353685+0800 threadTest[2739:696981] 售票線程B賣出去了一張票,還剩下0張票
11:32:06.353711+0800 threadTest[2739:696981] 售票線程B解鎖
11:32:06.353741+0800 threadTest[2739:696981] 售票線程B準(zhǔn)備上鎖
11:32:06.353772+0800 threadTest[2739:696981] 售票線程B已上鎖
11:32:06.356014+0800 threadTest[2739:696981] 售票線程B:票已售完
11:32:06.356058+0800 threadTest[2739:696981] 售票線程B解鎖
11:32:06.356621+0800 threadTest[2739:696809] 售票線程A已上鎖
11:32:06.357062+0800 threadTest[2739:696809] 售票線程A:票已售完
11:32:06.357103+0800 threadTest[2739:696809] 售票線程A解鎖

OSSpinLockTry 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO

-(void)saleTicket2{
    while (1) {
//        [NSThread sleepForTimeInterval:1];
        NSLog(@"%@準(zhǔn)備上鎖",[NSThread currentThread].name);
//        OSSpinLockLock(&spinLock);
        
        //嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
        //當(dāng)前線程鎖失敗,也可以繼續(xù)其它任務(wù),用 OSSpinLockTry 合適
        //當(dāng)前線程只有鎖成功后,才會(huì)做一些有意義的工作,那就OSSpinLockLock,沒必要輪詢OSSpinLockTry
        if (OSSpinLockTry(&spinLock)) {
            NSLog(@"%@已上鎖",[NSThread currentThread].name);
            if (self.totalCount > 0) {
                self.totalCount --;
                NSLog(@"%@賣出去了一張票,還剩下%zd張票", [NSThread currentThread].name,self.totalCount);
                OSSpinLockUnlock(&spinLock);
                NSLog(@"%@解鎖",[NSThread currentThread].name);
            }else{
                NSLog(@"%@:票已售完",[NSThread currentThread].name);
                OSSpinLockUnlock(&spinLock);
                NSLog(@"%@解鎖",[NSThread currentThread].name);
                break;
            }
        }else{
            NSLog(@"%@等待中",[NSThread currentThread].name);
        }
    }
}

輸出結(jié)果

13:53:40.635663+0800 threadTest[296:19536] 售票線程A準(zhǔn)備上鎖
13:53:40.635689+0800 threadTest[296:19536] 售票線程A已上鎖
13:53:40.635717+0800 threadTest[296:19536] 售票線程A賣出去了一張票,還剩下2張票
13:53:40.635929+0800 threadTest[296:19536] 售票線程A解鎖
13:53:40.635967+0800 threadTest[296:19536] 售票線程A準(zhǔn)備上鎖
13:53:40.635773+0800 threadTest[296:19780] 售票線程B準(zhǔn)備上鎖
13:53:40.635993+0800 threadTest[296:19536] 售票線程A已上鎖
13:53:40.636022+0800 threadTest[296:19536] 售票線程A賣出去了一張票,還剩下1張票
13:53:40.636025+0800 threadTest[296:19780] 售票線程B等待中
13:53:40.636049+0800 threadTest[296:19536] 售票線程A解鎖
13:53:40.636064+0800 threadTest[296:19780] 售票線程B準(zhǔn)備上鎖
13:53:40.636075+0800 threadTest[296:19536] 售票線程A準(zhǔn)備上鎖
13:53:40.636093+0800 threadTest[296:19780] 售票線程B已上鎖
13:53:40.636102+0800 threadTest[296:19536] 售票線程A等待中
13:53:40.636126+0800 threadTest[296:19780] 售票線程B賣出去了一張票,還剩下0張票
13:53:40.636160+0800 threadTest[296:19780] 售票線程B解鎖
13:53:40.636520+0800 threadTest[296:19780] 售票線程B準(zhǔn)備上鎖
13:53:40.636548+0800 threadTest[296:19780] 售票線程B已上鎖
13:53:40.636575+0800 threadTest[296:19780] 售票線程B:票已售完
13:53:40.636600+0800 threadTest[296:19780] 售票線程B解鎖
13:53:40.636129+0800 threadTest[296:19536] 售票線程A準(zhǔn)備上鎖
13:53:40.636652+0800 threadTest[296:19536] 售票線程A已上鎖
13:53:40.636678+0800 threadTest[296:19536] 售票線程A:票已售完
13:53:40.636704+0800 threadTest[296:19536] 售票線程A解鎖

2.os_unfair_lock(iOS 10以后,蘋果提供用來代替OSSpinLock)

測(cè)試代碼和OSSpinLock相同 替換對(duì)應(yīng)上鎖解鎖代碼

#import <os/lock.h>

// 初始化(os_unfair_lock_t)
os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT);
// 加鎖
os_unfair_lock_lock(unfairLock);
// 解鎖
os_unfair_lock_unlock(unfairLock);
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
os_unfair_lock_trylock(unfairLock)
//或者
// 初始化(os_unfair_lock)
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);

3.dispatch_semaphore (信號(hào)量)

dispatch_semaphore_create(1):創(chuàng)建信號(hào)量,傳入值必須 >=0, 若傳入為 0 則阻塞線程并等待timeout,時(shí)間到后會(huì)執(zhí)行其后的語句
dispatch_semaphore_wait(signal, overTime):等待信號(hào)量,可以理解為 lock,會(huì)使得 signal 值 -1
dispatch_semaphore_signal(signal):發(fā)送信號(hào)量,可以理解為 unlock,會(huì)使得 signal 值 +1

關(guān)于信號(hào)量,在其他文章中看到的一個(gè)比較形象的比喻:

停車場(chǎng)剩余4個(gè)車位,那么即使同時(shí)來了四輛車也能停的下。如果此時(shí)來了五輛車,那么就有一輛需要等待。
信號(hào)量的值(signal)就相當(dāng)于剩余車位的數(shù)目,dispatch_semaphore_wait 函數(shù)就相當(dāng)于來了一輛車,dispatch_semaphore_signal 就相當(dāng)于走了一輛車。停車位的剩余數(shù)目在初始化的時(shí)候就已經(jīng)指明了(dispatch_semaphore_create(long value)),調(diào)用一次 dispatch_semaphore_signal,剩余的車位就增加一個(gè);調(diào)用一次dispatch_semaphore_wait 剩余車位就減少一個(gè);當(dāng)剩余車位為 0 時(shí),再來車(即調(diào)用 dispatch_semaphore_wait)就只能等待。有可能同時(shí)有幾輛車等待一個(gè)停車位。有些車主沒有耐心,給自己設(shè)定了一段等待時(shí)間,這段時(shí)間內(nèi)等不到停車位就走了,如果等到了就開進(jìn)去停車。而有些車主就像把車停在這,所以就一直等下去。

dispatch_semaphore測(cè)試代碼01 - 簡(jiǎn)單模擬停車

    dispatch_queue_t queue = dispatch_queue_create("dispatch_semaphore_test2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);// 停車場(chǎng)車位數(shù)
    
    for (int i = 0; i < 4; i ++) {
        [NSThread currentThread].name = [NSString stringWithFormat:@"汽車%d",i+1];
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"%@進(jìn)入",[NSThread currentThread]);
            int x = arc4random() % 4;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((x+1) * NSEC_PER_SEC)), queue, ^{
            [NSThread currentThread].name = [NSString stringWithFormat:@"汽車%d",i+1];
            NSLog(@"%@停車%d秒后離開",[NSThread currentThread],(x+1));
            dispatch_semaphore_signal(semaphore);
        });
    }
    NSLog(@"----------------------------");

輸出結(jié)果

13:17:19.709194+0800 threadTest[930:177270] <NSThread: 0x280d0d8c0>{number = 1, name = 汽車1}進(jìn)入
13:17:19.710074+0800 threadTest[930:177270] <NSThread: 0x280d0d8c0>{number = 1, name = 汽車2}進(jìn)入
13:17:21.888932+0800 threadTest[930:177395] <NSThread: 0x280d608c0>{number = 3, name = 汽車1}停車2秒后離開
13:17:21.889121+0800 threadTest[930:177270] <NSThread: 0x280d0d8c0>{number = 1, name = 汽車3}進(jìn)入
13:17:24.098562+0800 threadTest[930:177515] <NSThread: 0x280d5b100>{number = 4, name = 汽車2}停車4秒后離開
13:17:24.099205+0800 threadTest[930:177270] <NSThread: 0x280d0d8c0>{number = 1, name = 汽車4}進(jìn)入
13:17:24.099399+0800 threadTest[930:177270] ----------------------------
13:17:26.289179+0800 threadTest[930:177395] <NSThread: 0x280d608c0>{number = 3, name = 汽車3}停車4秒后離開
13:17:26.289179+0800 threadTest[930:177515] <NSThread: 0x280d5b100>{number = 4, name = 汽車4}停車2秒后離開

dispatch_semaphore測(cè)試代碼02
多個(gè)異步請(qǐng)求完成之后再執(zhí)行最后的任務(wù)(用dispatch_group_enter(dispatch_group_t group) 和 dispatch_group_leave(dispatch_group_t group)也可以實(shí)現(xiàn))

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("dispatch_semaphore_test1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"任務(wù)01開始%@",[NSThread currentThread]);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"任務(wù)01完成%@",[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        NSLog(@"任務(wù)01等待完成%@",[NSThread currentThread]);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"任務(wù)02開始%@",[NSThread currentThread]);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"任務(wù)02完成%@",[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        NSLog(@"任務(wù)02等待完成%@",[NSThread currentThread]);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"所有任務(wù)已完成");
    });

輸出結(jié)果

18:13:02.689950+0800 threadTest[540:66884] 任務(wù)01開始<NSThread: 0x28075cec0>{number = 4, name = (null)}
18:13:02.690544+0800 threadTest[540:66884] 任務(wù)01等待完成<NSThread: 0x28075cec0>{number = 4, name = (null)}
18:13:02.690597+0800 threadTest[540:66944] 任務(wù)02開始<NSThread: 0x28075cb40>{number = 3, name = (null)}
18:13:02.690754+0800 threadTest[540:66944] 任務(wù)02等待完成<NSThread: 0x28075cb40>{number = 3, name = (null)}
18:13:03.737854+0800 threadTest[540:66847] 任務(wù)02完成<NSThread: 0x280735a40>{number = 1, name = main}
18:13:04.837859+0800 threadTest[540:66847] 任務(wù)01完成<NSThread: 0x280735a40>{number = 1, name = main}
18:13:04.838539+0800 threadTest[540:66847] 所有任務(wù)已完成
18:13:11.508223+0800 threadTest[540:66944] 任務(wù)01開始<NSThread: 0x28075cb40>{number = 3, name = (null)}
18:13:11.508920+0800 threadTest[540:66964] 任務(wù)02開始<NSThread: 0x28075c800>{number = 5, name = (null)}
18:13:11.509366+0800 threadTest[540:66944] 任務(wù)01等待完成<NSThread: 0x28075cb40>{number = 3, name = (null)}
18:13:11.509425+0800 threadTest[540:66964] 任務(wù)02等待完成<NSThread: 0x28075c800>{number = 5, name = (null)}
18:13:12.537780+0800 threadTest[540:66847] 任務(wù)02完成<NSThread: 0x280735a40>{number = 1, name = main}
18:13:13.654482+0800 threadTest[540:66847] 任務(wù)01完成<NSThread: 0x280735a40>{number = 1, name = main}
18:13:13.655111+0800 threadTest[540:66847] 所有任務(wù)已完成

4.pthread_mutex(互斥鎖)

測(cè)試代碼和OSSpinLock相同 替換對(duì)應(yīng)上鎖解鎖代碼

#import <pthread/pthread.h>

// 普通初始化
pthread_mutex_t mutexLock;
pthread_mutex_init(&mutexLock, NULL); 
// 宏初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 加鎖
pthread_mutex_lock(&mutexLock);
// 解鎖
pthread_mutex_unlock(&mutexLock);
// 嘗試加鎖,可以加鎖時(shí)返回的是 0,否則返回一個(gè)錯(cuò)誤(OSSpinLockTry(&spinLock)返回YES或者NO)
pthread_mutex_trylock(&mutexLock)

5.pthread_mutex(recursive) (遞歸鎖)

注: 遞歸鎖可以被同一線程多次請(qǐng)求,而不會(huì)引起死鎖。即在同一線程中在未解鎖之前還可以上鎖, 執(zhí)行鎖中的代碼。這主要是用在循環(huán)或遞歸操作中。
pthread_mutex_t mutex_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); //初始化attr
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); //設(shè)置鎖類型,這邊是設(shè)置為遞歸鎖
pthread_mutex_init(&mutex_t, &attr);
pthread_mutexattr_destroy(&attr); //銷毀一個(gè)屬性對(duì)象,在重新進(jìn)行初始化之前該結(jié)構(gòu)不能重新使用
pthread_mutex_lock(&mutex_t);// 加鎖
pthread_mutex_unlock(&mutex_t);// 解鎖
pthread_mutexattr_t設(shè)置的相關(guān)函數(shù)及其說明

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self recursive:2];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self recursive:2];
});

-(void)recursive:(int)flag{
    pthread_mutex_lock(&_pLock);
    sleep(1);
    if (flag > 0) {
        NSLog(@"%@請(qǐng)求%d", [NSThread currentThread], flag);
        [self recursive:(--flag)];
    }else{
        flag--;
        NSLog(@"%@請(qǐng)求成功", [NSThread currentThread]);
    }
    pthread_mutex_unlock(&_pLock);
    NSLog(@"遞歸解鎖%d", flag + 1);
}

// 輸出結(jié)果
15:02:17.337545+0800 threadTest[1096:242086] <NSThread: 0x281e7a0c0>{number = 3, name = (null)}請(qǐng)求2
15:02:18.343244+0800 threadTest[1096:242086] <NSThread: 0x281e7a0c0>{number = 3, name = (null)}請(qǐng)求1
15:02:19.348308+0800 threadTest[1096:242086] <NSThread: 0x281e7a0c0>{number = 3, name = (null)}請(qǐng)求成功
15:02:19.348655+0800 threadTest[1096:242086] 遞歸解鎖0
15:02:19.348980+0800 threadTest[1096:242086] 遞歸解鎖1
15:02:19.349154+0800 threadTest[1096:242086] 遞歸解鎖2
15:02:20.355346+0800 threadTest[1096:242091] <NSThread: 0x281e468c0>{number = 4, name = (null)}請(qǐng)求2
15:02:21.356200+0800 threadTest[1096:242091] <NSThread: 0x281e468c0>{number = 4, name = (null)}請(qǐng)求1
15:02:22.361770+0800 threadTest[1096:242091] <NSThread: 0x281e468c0>{number = 4, name = (null)}請(qǐng)求成功
15:02:22.361986+0800 threadTest[1096:242091] 遞歸解鎖0
15:02:22.362130+0800 threadTest[1096:242091] 遞歸解鎖1
15:02:22.362502+0800 threadTest[1096:242091] 遞歸解鎖2

6.NSLock(互斥鎖)

NSLock 是 Objective-C 以對(duì)象的形式暴露給開發(fā)者的一種鎖,只是在內(nèi)部封裝了一個(gè) pthread_mutex,屬性為 PTHREAD_MUTEX_ERRORCHECK,它會(huì)損失一定性能換來錯(cuò)誤提示。NSLock 比 pthread_mutex 略慢的原因在于它需要經(jīng)過方法調(diào)用,同時(shí)由于緩存的存在,多次方法調(diào)用不會(huì)對(duì)性能產(chǎn)生太大的影響。

測(cè)試代碼和OSSpinLock相同 替換對(duì)應(yīng)上鎖解鎖代碼

// 初始化
NSLock *lock = [[NSLock alloc] init];
[lock lock];// 加鎖
[lock unlock];// 解鎖
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[lock tryLock];
// 這個(gè)方法表示會(huì)在傳入的時(shí)間內(nèi)嘗試加鎖,若能加鎖則執(zhí)行加鎖操作并返回 YES,反之返回 NO
[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];

7. NSCondition(條件鎖)

_condition = [[NSCondition alloc] init];// 初始化
[_condition lock];// 加鎖
[_condition unlock];// 解鎖
[_condition wait];// 進(jìn)入等待狀態(tài)
[_condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];// 讓一個(gè)線程等待一定的時(shí)間
[_condition signal];// 喚醒一個(gè)等待的線程
[_condition broadcast];// 喚醒所有等待的線程

使用NSCondition模擬售票,并增加補(bǔ)充車票功能

    BOOL adding;// 是否正在補(bǔ)充車票
    BOOL flag;// 是否還能補(bǔ)充車票
----------------------------------------------
    //設(shè)置票數(shù)
    self.totalCount = 2;
    adding = NO;
    flag = YES;
    // 初始化
    _condition = [[NSCondition alloc] init];
    _condition.name = @"售票";

    dispatch_queue_t queue = dispatch_queue_create("saleTicket5", DISPATCH_QUEUE_CONCURRENT);
    //售票線程1
    dispatch_async(queue, ^{
        [NSThread currentThread].name = @"售票線程A";
        [self saleTicket5];
    });
    NSLog(@"-----------------------");
    //售票線程2
    dispatch_async(queue, ^{
        [NSThread currentThread].name = @"售票線程B";
        [self saleTicket5];
    });
-(void)saleTicket5{
    while (1) {
        NSLog(@"%@準(zhǔn)備上鎖",[NSThread currentThread].name);
        [_condition lock];
        NSLog(@"%@已上鎖",[NSThread currentThread].name);
        if (self.totalCount > 0) {
            self.totalCount --;
            NSLog(@"%@賣出去了一張票,還剩下%zd張票", [NSThread currentThread].name,self.totalCount);
            [_condition unlock];
            NSLog(@"%@解鎖",[NSThread currentThread].name);
        }else{
            NSLog(@"%@:票已售完,等待補(bǔ)充車票",[NSThread currentThread].name);
            if (!adding) {
                adding = YES;
                dispatch_async(dispatch_queue_create("addTicket5", DISPATCH_QUEUE_CONCURRENT), ^{
                    [NSThread currentThread].name = @"添加車票線程";
                    [self addTicket5];
                });
            }
            [_condition wait];// 進(jìn)入等待狀態(tài)
            if (self.totalCount > 0) {
                NSLog(@"%@:獲取到新車票",[NSThread currentThread].name);
                [_condition unlock];
                NSLog(@"%@解鎖",[NSThread currentThread].name);
            }else{
                NSLog(@"%@:票已售完",[NSThread currentThread].name);
                [_condition unlock];
                NSLog(@"%@解鎖",[NSThread currentThread].name);
                break;
            }
        }
    }
}
-(void)addTicket5{
    if (self.totalCount <= 0 && flag) {
        self.totalCount += 2;
        NSLog(@"%@已補(bǔ)充車票",[NSThread currentThread].name);
        flag = NO;
    }else{
        self.totalCount = 0;
        NSLog(@"%@無車票補(bǔ)充",[NSThread currentThread].name);
    }
    [_condition broadcast];
    NSLog(@"%@喚醒所有等待售票線程",[NSThread currentThread].name);
    adding = NO;
}

輸出結(jié)果

17:05:08.425699+0800 threadTest[1173:260434] -----------------------
17:05:08.430148+0800 threadTest[1173:260571] 售票線程A準(zhǔn)備上鎖
17:05:08.430309+0800 threadTest[1173:260571] 售票線程A已上鎖
17:05:08.430454+0800 threadTest[1173:260571] 售票線程A賣出去了一張票,還剩下1張票
17:05:08.430582+0800 threadTest[1173:260571] 售票線程A解鎖
17:05:08.430703+0800 threadTest[1173:260571] 售票線程A準(zhǔn)備上鎖
17:05:08.430836+0800 threadTest[1173:260571] 售票線程A已上鎖
17:05:08.430968+0800 threadTest[1173:260571] 售票線程A賣出去了一張票,還剩下0張票
17:05:08.431088+0800 threadTest[1173:260571] 售票線程A解鎖
17:05:08.431205+0800 threadTest[1173:260571] 售票線程A準(zhǔn)備上鎖
17:05:08.431324+0800 threadTest[1173:260571] 售票線程A已上鎖
17:05:08.431361+0800 threadTest[1173:260572] 售票線程B準(zhǔn)備上鎖
17:05:08.431445+0800 threadTest[1173:260571] 售票線程A:票已售完,等待補(bǔ)充車票
17:05:08.432165+0800 threadTest[1173:260572] 售票線程B已上鎖
17:05:08.432285+0800 threadTest[1173:260572] 售票線程B:票已售完,等待補(bǔ)充車票
17:05:08.432427+0800 threadTest[1173:260535] 添加車票線程已補(bǔ)充車票
17:05:08.432530+0800 threadTest[1173:260535] 添加車票線程喚醒所有等待售票線程
17:05:08.432629+0800 threadTest[1173:260571] 售票線程A:獲取到新車票
17:05:08.432724+0800 threadTest[1173:260571] 售票線程A解鎖
17:05:08.432788+0800 threadTest[1173:260571] 售票線程A準(zhǔn)備上鎖
17:05:08.432864+0800 threadTest[1173:260571] 售票線程A已上鎖
17:05:08.433021+0800 threadTest[1173:260571] 售票線程A賣出去了一張票,還剩下1張票
17:05:08.433543+0800 threadTest[1173:260571] 售票線程A解鎖
17:05:08.433577+0800 threadTest[1173:260572] 售票線程B:獲取到新車票
17:05:08.436824+0800 threadTest[1173:260572] 售票線程B解鎖
17:05:08.437085+0800 threadTest[1173:260572] 售票線程B準(zhǔn)備上鎖
17:05:08.437181+0800 threadTest[1173:260572] 售票線程B已上鎖
17:05:08.437260+0800 threadTest[1173:260572] 售票線程B賣出去了一張票,還剩下0張票
17:05:08.437408+0800 threadTest[1173:260572] 售票線程B解鎖
17:05:08.437475+0800 threadTest[1173:260572] 售票線程B準(zhǔn)備上鎖
17:05:08.437540+0800 threadTest[1173:260572] 售票線程B已上鎖
17:05:08.437606+0800 threadTest[1173:260572] 售票線程B:票已售完,等待補(bǔ)充車票
17:05:08.433616+0800 threadTest[1173:260571] 售票線程A準(zhǔn)備上鎖
17:05:08.437749+0800 threadTest[1173:260571] 售票線程A已上鎖
17:05:08.437815+0800 threadTest[1173:260571] 售票線程A:票已售完,等待補(bǔ)充車票
17:05:08.438012+0800 threadTest[1173:260535] 添加車票線程無車票補(bǔ)充
17:05:08.438456+0800 threadTest[1173:260535] 添加車票線程喚醒所有等待售票線程
17:05:08.438559+0800 threadTest[1173:260572] 售票線程B:票已售完
17:05:08.438628+0800 threadTest[1173:260572] 售票線程B解鎖
17:05:08.438722+0800 threadTest[1173:260571] 售票線程A:票已售完
17:05:08.438787+0800 threadTest[1173:260571] 售票線程A解鎖

8. NSRecursiveLock(遞歸鎖)

NSRecursiveLock也是通過 pthread_mutex_lock 函數(shù)來實(shí)現(xiàn),NSRecursiveLock 與 NSLock 的區(qū)別在于內(nèi)部封裝的 pthread_mutex_t 對(duì)象的類型不同,NSRecursiveLock的類型為 PTHREAD_MUTEX_RECURSIVE。

測(cè)試代碼和pthread_mutex(recursive) 相同 替換對(duì)應(yīng)上鎖解鎖代碼

NSRecursiveLock *_recursiveLock = [[NSRecursiveLock alloc] init];// 初始化
[_recursiveLock lock];// 加鎖
[_recursiveLock unlock];// 解鎖
[_recursiveLock tryLock];// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_recursiveLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];// 這個(gè)方法表示會(huì)在傳入的時(shí)間內(nèi)嘗試加鎖,若能加鎖則執(zhí)行加鎖操作并返回 YES,反之返回 NO

9. NSConditionLock(條件鎖)

NSConditionLock *_conditionLock = [[NSConditionLock alloc] init];// 初始化
[_conditionLock lock];// 加鎖
[_conditionLock unlock];// 解鎖
[_conditionLock tryLock];// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
-(void)lockWhenCondition:(NSInteger)condition;//條件成立觸發(fā)鎖,會(huì)阻塞當(dāng)前線程
-(BOOL)tryLockWhenCondition:(NSInteger)condition;//嘗試條件成立觸發(fā)鎖
-(void)unlockWithCondition:(NSInteger)condition;//條件成立解鎖
-(BOOL)lockBeforeDate:(NSDate *)limit;//觸發(fā)鎖 在等待時(shí)間之內(nèi),會(huì)阻塞當(dāng)前線程
-(BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//觸發(fā)鎖 條件成立 并且在等待時(shí)間之內(nèi),會(huì)阻塞當(dāng)前線程

    NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:1];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        [lock lockWhenCondition:1];
        NSLog(@"任務(wù)01開始%@",[NSThread currentThread]);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"任務(wù)01完成%@",[NSThread currentThread]);
            [lock unlockWithCondition:3];
        });
        NSLog(@"任務(wù)01等待完成%@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//        if ([lock lockWhenCondition:2]) {
        if ([lock lockWhenCondition:2 beforeDate:[NSDate dateWithTimeIntervalSinceNow:6]]) {
            NSLog(@"任務(wù)02開始%@",[NSThread currentThread]);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                NSLog(@"任務(wù)02完成%@",[NSThread currentThread]);
                [lock unlockWithCondition:4];
            });
            NSLog(@"任務(wù)02等待完成%@",[NSThread currentThread]);
        }else{
            [lock lockWhenCondition:4];
            NSLog(@"任務(wù)02開始%@",[NSThread currentThread]);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                NSLog(@"任務(wù)02完成%@",[NSThread currentThread]);
                [lock unlockWithCondition:5];
            });
            NSLog(@"任務(wù)02等待完成%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//        sleep(3);
        if ([lock tryLockWhenCondition:3]) {
            NSLog(@"任務(wù)03開始%@",[NSThread currentThread]);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                NSLog(@"任務(wù)03完成%@",[NSThread currentThread]);
                [lock unlockWithCondition:2];
            });
            NSLog(@"任務(wù)03等待完成%@",[NSThread currentThread]);
        }else{
            [lock lockWhenCondition:3];
            NSLog(@"任務(wù)03失敗%@",[NSThread currentThread]);
            [lock unlockWithCondition:4];
        }
    });

輸出結(jié)果

11:31:32.311166+0800 threadTest[1541:400982] 任務(wù)01開始<NSThread: 0x2832308c0>{number = 3, name = (null)}
11:31:32.311597+0800 threadTest[1541:400982] 任務(wù)01等待完成<NSThread: 0x2832308c0>{number = 3, name = (null)}
11:31:34.497140+0800 threadTest[1541:401005] 任務(wù)01完成<NSThread: 0x283239540>{number = 4, name = (null)}
11:31:34.497599+0800 threadTest[1541:401003] 任務(wù)03失敗<NSThread: 0x283220200>{number = 5, name = (null)}
11:31:38.316952+0800 threadTest[1541:400982] 任務(wù)02開始<NSThread: 0x2832308c0>{number = 3, name = (null)}
11:31:38.317327+0800 threadTest[1541:400982] 任務(wù)02等待完成<NSThread: 0x2832308c0>{number = 3, name = (null)}
11:31:39.372796+0800 threadTest[1541:400982] 任務(wù)02完成<NSThread: 0x2832308c0>{number = 3, name = (null)}

10. @synchronized(條件鎖)

@synchronized是一個(gè) OC 層面的鎖, 主要是通過犧牲性能換來語法上的簡(jiǎn)潔與可讀。我們知道 @synchronized 后面需要緊跟一個(gè) OC 對(duì)象,它實(shí)際上是把這個(gè)對(duì)象當(dāng)做鎖來使用。這是通過一個(gè)哈希表來實(shí)現(xiàn)的,OC 在底層使用了一個(gè)互斥鎖的數(shù)組(你可以理解為鎖池),通過對(duì)對(duì)象去哈希值來得到對(duì)應(yīng)的互斥鎖。

    //設(shè)置票數(shù)
    self.totalCount = 10;
    dispatch_queue_t queue = dispatch_queue_create("saleTicket", DISPATCH_QUEUE_CONCURRENT);
    //售票線程1
    dispatch_async(queue, ^{
        [NSThread currentThread].name = @"售票線程A";
        while (1) {
            sleep(1);
            if (![self saleTicket6]) {
                break;
            }
        }
    });
    //售票線程2
    dispatch_async(queue, ^{
        [NSThread currentThread].name = @"售票線程B";
        while (1) {
            sleep(2);
            if (![self saleTicket6]) {
                break;
            }
        }
    });


-(BOOL)saleTicket6{
    @synchronized(self) {
        if (self.totalCount > 0) {
            self.totalCount --;
            NSLog(@"%@賣出去了一張票,還剩下%zd張票", [NSThread currentThread].name,self.totalCount);
            return YES;
        }else{
            NSLog(@"%@:票已售完",[NSThread currentThread].name);
            return NO;
        }
    }
    
}

輸出結(jié)果

13:54:39.353828+0800 threadTest[1604:420440] 售票線程A賣出去了一張票,還剩下9張票
13:54:40.354401+0800 threadTest[1604:420478] 售票線程B賣出去了一張票,還剩下8張票
13:54:40.359244+0800 threadTest[1604:420440] 售票線程A賣出去了一張票,還剩下7張票
13:54:41.363755+0800 threadTest[1604:420440] 售票線程A賣出去了一張票,還剩下6張票
13:54:42.354886+0800 threadTest[1604:420478] 售票線程B賣出去了一張票,還剩下5張票
13:54:42.366684+0800 threadTest[1604:420440] 售票線程A賣出去了一張票,還剩下4張票
13:54:43.372029+0800 threadTest[1604:420440] 售票線程A賣出去了一張票,還剩下3張票
13:54:44.360261+0800 threadTest[1604:420478] 售票線程B賣出去了一張票,還剩下2張票
13:54:44.373056+0800 threadTest[1604:420440] 售票線程A賣出去了一張票,還剩下1張票
13:54:45.373976+0800 threadTest[1604:420440] 售票線程A賣出去了一張票,還剩下0張票
13:54:46.364459+0800 threadTest[1604:420478] 售票線程B:票已售完
13:54:46.374672+0800 threadTest[1604:420440] 售票線程A:票已售完

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

11. pthread_rwlock(讀寫鎖)

讀寫鎖是用來解決讀者寫者問題的,讀操作可以共享,寫操作是排他的,讀可以有多個(gè)在讀,寫只有唯一個(gè)在寫,同時(shí)寫的時(shí)候不允許讀。
對(duì)于讀數(shù)據(jù)比修改數(shù)據(jù)頻繁的應(yīng)用,用讀寫鎖代替互斥鎖可以提高效率。因?yàn)槭褂没コ怄i時(shí),即使是讀出數(shù)據(jù)(相當(dāng)于操作臨界區(qū)資源)都要上互斥鎖,而采用讀寫鎖,則可以在任一時(shí)刻允許多個(gè)讀出者存在,提高了更高的并發(fā)度,同時(shí)在某個(gè)寫入者修改數(shù)據(jù)期間保護(hù)該數(shù)據(jù),以免任何其它讀出者或?qū)懭胝叩母蓴_。

//初始化attr
pthread_rwlockattr_t rwlockattr;
pthread_rwlockattr_init(&rwlockattr);
//初始化
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_init(&rwlock, &rwlockattr);
//加讀鎖 阻塞線程
pthread_rwlock_rdlock(&rwlock);
//嘗試加讀鎖 不阻塞線程
pthread_rwlock_tryrdlock(&rwlock);
//加寫鎖 阻塞線程
pthread_rwlock_wrlock(&rwlock);
//嘗試加寫鎖 不阻塞線程
pthread_rwlock_trywrlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
//銷毀
pthread_rwlock_destroy(&rwlock);
pthread_rwlockattr_destroy(&rwlockattr);

    pthread_rwlock_init(&rwlock, NULL);
    _document = @"123";
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread currentThread].name = @"線程A";
        [self read];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread currentThread].name = @"線程B";
        [self write];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread currentThread].name = @"線程C";
        [self read];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread currentThread].name = @"線程D";
        [self write];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread currentThread].name = @"線程E";
        [self read];
    });
-(void)read{
    NSLog(@"%@準(zhǔn)備查看",[NSThread currentThread].name);
    pthread_rwlock_rdlock(&rwlock);
    NSLog(@"%@查看內(nèi)容為:%@",[NSThread currentThread].name,_document);
    sleep(1);
    pthread_rwlock_unlock(&rwlock);
    NSLog(@"%@結(jié)束查看",[NSThread currentThread].name);
}
-(void)write{
    NSLog(@"%@準(zhǔn)備修改",[NSThread currentThread].name);
    pthread_rwlock_wrlock(&rwlock);
    _document = [NSString stringWithFormat:@"%@修正為321",[NSThread currentThread].name];
    NSLog(@"%@",_document);
    sleep(1);
    pthread_rwlock_unlock(&rwlock);
    NSLog(@"%@完成修改",[NSThread currentThread].name);
}

輸出結(jié)果

14:48:22.750376+0800 threadTest[1623:427484] 線程B準(zhǔn)備修改
14:48:22.750646+0800 threadTest[1623:427484] 線程B修正為321
14:48:22.750827+0800 threadTest[1623:427485] 線程A準(zhǔn)備查看
14:48:22.751105+0800 threadTest[1623:427487] 線程C準(zhǔn)備查看
14:48:22.751798+0800 threadTest[1623:427486] 線程D準(zhǔn)備修改
14:48:22.752112+0800 threadTest[1623:427498] 線程E準(zhǔn)備查看
14:48:23.755845+0800 threadTest[1623:427484] 線程B完成修改
14:48:23.755920+0800 threadTest[1623:427485] 線程A查看內(nèi)容為:線程B修正為321
14:48:23.755941+0800 threadTest[1623:427487] 線程C查看內(nèi)容為:線程B修正為321
14:48:24.760281+0800 threadTest[1623:427485] 線程A結(jié)束查看
14:48:24.760842+0800 threadTest[1623:427487] 線程C結(jié)束查看
14:48:24.761085+0800 threadTest[1623:427486] 線程D修正為321
14:48:25.765181+0800 threadTest[1623:427486] 線程D完成修改
14:48:25.765313+0800 threadTest[1623:427498] 線程E查看內(nèi)容為:線程D修正為321
14:48:26.766249+0800 threadTest[1623:427498] 線程E結(jié)束查看

參考資料

iOS 開發(fā)中的八種鎖(Lock)
深入理解 iOS 開發(fā)中的鎖
iOS 十種線程鎖

最后編輯于
?著作權(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)容

  • 鎖是一種同步機(jī)制,用于多線程環(huán)境中對(duì)資源訪問的限制iOS中常見鎖的性能對(duì)比圖(摘自:ibireme): iOS鎖的...
    LiLS閱讀 1,627評(píng)論 0 6
  • 本文節(jié)選自成長(zhǎng)手冊(cè) 文章推薦和參考深入理解 iOS 開發(fā)中的鎖pthread的各種同步機(jī)制 多線程編程被普遍認(rèn)為復(fù)...
    百草紀(jì)閱讀 2,906評(píng)論 1 9
  • 為什么要有鎖? 在使用多線程的時(shí)候多個(gè)線程可能會(huì)訪問同一塊資源,這樣就很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全等問題,這時(shí)候就...
    153037c65b0c閱讀 609評(píng)論 0 1
  • 線程安全是怎么產(chǎn)生的 常見比如線程內(nèi)操作了一個(gè)線程外的非線程安全變量,這個(gè)時(shí)候一定要考慮線程安全和同步。 - (v...
    幽城88閱讀 771評(píng)論 0 0
  • 前言 iOS開發(fā)中由于各種第三方庫的高度封裝,對(duì)鎖的使用很少,剛好之前面試中被問到的關(guān)于并發(fā)編程鎖的問題,都是一知...
    喵渣渣閱讀 3,864評(píng)論 0 33

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