中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

iOS 常見(jiàn)知識(shí)點(diǎn)

2018-07-20    來(lái)源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用

鎖是最常用的同步工具。一段代碼段在同一個(gè)時(shí)間只能允許被有限個(gè)線程訪問(wèn),比如一個(gè)線程 A 進(jìn)入需要保護(hù)代碼之前添加簡(jiǎn)單的互斥鎖,另一個(gè)線程 B 就無(wú)法訪問(wèn),只有等待前一個(gè)線程 A 執(zhí)行完被保護(hù)的代碼后解鎖,B 線程才能訪問(wèn)被保護(hù)代碼。

iOS 中的八大鎖

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之前嘗試加鎖,如果在指定時(shí)間之前都不能加鎖,則返回NO。

舉個(gè):chestnut:

//主線程中
    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ù)代碼。

查到的資料顯示互斥鎖會(huì)使得線程阻塞,阻塞的過(guò)程又分兩個(gè)階段,第一階段是會(huì)先空轉(zhuǎn),可以理解成跑一個(gè) while 循環(huán),不斷地去申請(qǐng)加鎖,在空轉(zhuǎn)一定時(shí)間之后,線程會(huì)進(jìn)入 waiting 狀態(tài),此時(shí)線程就不占用CPU資源了,等鎖可用的時(shí)候,這個(gè)線程會(huì)立即被喚醒。

所以如果將上面線程 1 中的 sleep(2); 改成 sleep(10); 輸出的結(jié)果會(huì)變成

2016-08-19 14:25:16.226 ThreadLockControlDemo[1773:131824] 線程1
2016-08-19 14:25:26.231 ThreadLockControlDemo[1773:131831] 線程2
2016-08-19 14:25:26.231 ThreadLockControlDemo[1773:131824] 線程1解鎖成功

從上面的兩個(gè)輸出結(jié)果可以看出,線程 2 lock 的第一秒,是一直在輪詢請(qǐng)求加鎖的,因?yàn)檩喸冇袝r(shí)間間隔,所以 ”線程 2“ 的輸出晚于 ”線程 1 解鎖成功“,但線程 2 lock 的第九秒,是當(dāng)鎖可用的時(shí)候,立即被喚醒,所以 ”線程 2“ 的輸出早于 ”線程 1 解鎖成功“。多做了幾次試驗(yàn),發(fā)現(xiàn)輪詢 1 秒之后,線程會(huì)進(jìn)入 waiting 狀態(tài)。

//主線程中
    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 并不會(huì)阻塞線程。[lock tryLock] 能加鎖返回 YES,不能加鎖返回 NO,然后都會(huì)執(zhí)行后續(xù)代碼。

如果將 [lock tryLock] 替換成

[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]

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

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

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

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

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é)議,方法都類似,只是多了一個(gè) condition 屬性,以及每個(gè)操作都多了一個(gè)關(guān)于 condition 屬性的方法,例如 tryLock,tryLockWhenCondition:,NSConditionLock 可以稱為條件鎖,只有 condition 參數(shù)與初始化時(shí)候的 condition 相等,lock 才能正確進(jìn)行加鎖操作。而 unlockWithCondition: 并不是當(dāng) Condition 符合條件時(shí)才解鎖,而是解鎖之后,修改 Condition 的值,這個(gè)結(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“,因?yàn)榫程 1 的加鎖條件不滿足,初始化時(shí)候的 condition 參數(shù)為 0,而加鎖條件是 condition 為 1,所以加鎖失敗。locakWhenCondition 與 lock 方法類似,加鎖失敗會(huì)阻塞線程,所以線程 1 會(huì)被阻塞著,而 tryLockWhenCondition 方法就算條件不滿足,也會(huì)返回 NO,不會(huì)阻塞當(dāng)前線程。

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

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

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

從上面可以得出,NSConditionLock 還可以實(shí)現(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 可以在一個(gè)線程中重復(fù)加鎖(反正單線程內(nèi)任務(wù)是按順序執(zhí)行的,不會(huì)出現(xiàn)資源競(jìng)爭(zhēng)問(wèn)題),NSRecursiveLock 會(huì)記錄上鎖和解鎖的次數(shù),當(dāng)二者平衡的時(shí)候,才會(huì)釋放鎖,其它線程才可以上鎖成功。

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í)行解鎖的時(shí)候,就會(huì)進(jìn)入遞歸的下一層,而再次請(qǐng)求上鎖,阻塞了該線程,線程被阻塞了,自然后面的解鎖代碼不會(huì)執(zhí)行,而形成了死鎖。而 NSRecursiveLock 遞歸鎖就是為了解決這個(gè)問(wèn)題。

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

用法如下:

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];
    });

也就是使用 NSCondition 的模型為:

鎖定條件對(duì)象。

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

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

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

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

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

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

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

如果在 @sychronized(object){} 內(nèi)部 object 被釋放或被設(shè)為 nil,從我做的測(cè)試的結(jié)果來(lái)看,的確沒(méi)有問(wèn)題,但如果 object 一開(kāi)始就是 nil,則失去了鎖的功能。不過(guò)雖然 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 用來(lái)同步的一種方式,與他相關(guān)的只有三個(gè)函數(shù),一個(gè)是創(chuàng)建信號(hào)量,一個(gè)是等待信號(hào),一個(gè)是發(fā)送信號(hào)。

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);
    });

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

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

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

dispatch_semaphore_signal(signal); 發(fā)送信號(hào),如果沒(méi)有等待的線程接受信號(hào),則使 signal 信號(hào)值加一(做到對(duì)信號(hào)的保存)。

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

OSSpinLock

typedef int32_t OSSpinLock;

bool    OSSpinLockTry( volatile OSSpinLock *__lock );

void    OSSpinLockLock( volatile OSSpinLock *__lock );

void    OSSpinLockUnlock( volatile OSSpinLock *__lock );

OSSpinLock 是一種自旋鎖,也只有加鎖,解鎖,嘗試加鎖三個(gè)方法。和 NSLock 不同的是 NSLock 請(qǐng)求加鎖失敗的話,會(huì)先輪詢,但一秒過(guò)后便會(huì)使線程進(jìn)入 waiting 狀態(tài),等待喚醒。而 OSSpinLock 會(huì)一直輪詢,等待時(shí)會(huì)消耗大量 CPU 資源,不適用于較長(zhǎng)時(shí)間的任務(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é)果做對(duì)比,會(huì)發(fā)現(xiàn) sleep(10) 的情況,OSSpinLock 中的“線程 2”并沒(méi)有和”線程 1解鎖成功“在一個(gè)時(shí)間輸出,而 NSLock 這里是同一時(shí)間輸出,而是有一點(diǎn)時(shí)間間隔,所以 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 語(yǔ)言下多線程加互斥鎖的方式,那來(lái)段 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);

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

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

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

PTHREAD_MUTEX_RECURSIVE 遞歸鎖,允許同一個(gè)線程對(duì)同一個(gè)鎖成功獲得多次,并通過(guò)多次 unlock 解鎖。

PTHREAD_MUTEX_DEFAULT 適應(yīng)鎖,動(dòng)作最簡(jiǎn)單的鎖類型,僅等待解鎖后重新競(jìng)爭(zhēng),沒(méi)有等待隊(duì)列。

通過(guò) pthread_mutexattr_t 來(lái)設(shè)置鎖的類型,如下面代碼就設(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 用法一致,但還注意到有一個(gè) pthread_mutex_trylock 方法,pthread_mutex_trylock 和 tryLock 的區(qū)別在于,tryLock 返回的是 YES 和 NO,pthread_mutex_trylock 加鎖成功返回的是 0,失敗返回的是錯(cuò)誤提示碼。

pthread_mutex_destroy 為釋放鎖資源。

至于 pthread_mutex_setprioceiling 和 pthread_mutex_getprioceiling,懵逼臉,這兩個(gè)用來(lái)做什么不太理解。

性能

在 ibireme 的不再安全的 OSSpinLock 一文中,有貼出這些鎖的性能對(duì)比,如下圖:

來(lái)源:ibireme

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

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

從圖中可以知道 @synchronized 的效率最低,不過(guò)它的確用起來(lái)最方便,所以如果沒(méi)什么性能瓶頸的話,使用它也不錯(cuò)。

總結(jié):

雖然這些鎖看起來(lái)很復(fù)雜,但最終都是加鎖,等待,解鎖。一下子懂了八鎖,有點(diǎn)小激動(dòng)。

參考文章

iOS中保證線程安全的幾種方式與性能對(duì)比

不再安全的 OSSpinLock

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

 

來(lái)自:http://www.jianshu.com/p/ddbe44064ca4

標(biāo)簽: isp 安全 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:展望 C# 7

下一篇:C#函數(shù)式編程