iOS 多線程鎖

+++
Categories = ["iOS",]
Tags = ["iOS","Lock",]
date = "2015-08-27T17:35:29+08:00"
title = "iOS 多線程鎖"

+++

dispatch_semaphore

信號(hào)量是一個(gè)整形值并且具有一個(gè)初始計(jì)數(shù)值,并且支持兩個(gè)操作:信號(hào)通知和等待。當(dāng)一個(gè)信號(hào)量被信號(hào)通知,其計(jì)數(shù)會(huì)被增加。當(dāng)一個(gè)線程在一個(gè)信號(hào)量上等待時(shí),線程會(huì)被阻塞(如果有必要的話),直至計(jì)數(shù)器大于零,然后線程會(huì)減少這個(gè)計(jì)數(shù)。

在GCD中有三個(gè)函數(shù)是semaphore的操作,分別是:

  • dispatch_semaphore_create   創(chuàng)建一個(gè)semaphore
  • dispatch_semaphore_signal   發(fā)送一個(gè)信號(hào)
  • dispatch_semaphore_wait    等待信號(hào)

簡(jiǎn)單的介紹一下這三個(gè)函數(shù),第一個(gè)函數(shù)有一個(gè)整形的參數(shù),我們可以理解為信號(hào)的總量,dispatch_semaphore_signal是發(fā)送一個(gè)信號(hào),自然會(huì)讓信號(hào)總量加1,dispatch_semaphore_wait等待信號(hào),當(dāng)信號(hào)總量少于0的時(shí)候就會(huì)一直等待,否則就可以正常的執(zhí)行,并讓信號(hào)總量-1,根據(jù)這樣的原理,我們便可以快速的創(chuàng)建一個(gè)并發(fā)控制來(lái)同步任務(wù)和有限資源訪問控制。

dispatch_group_t group = dispatch_group_create();   
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);   
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   
    for (int i = 0; i < 100; i++)   
    {   
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);   
        dispatch_group_async(group, queue, ^{   
            NSLog(@"%i",i);   
            sleep(2);   
            dispatch_semaphore_signal(semaphore);   
        });   
    }   
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);   
    dispatch_release(group);   
    dispatch_release(semaphore);   

上面代碼創(chuàng)建了一個(gè)初使值為10的semaphore,每一次for循環(huán)都會(huì)創(chuàng)建一個(gè)新的線程,線程結(jié)束的時(shí)候會(huì)發(fā)送一個(gè)信號(hào),線程創(chuàng)建之前會(huì)信號(hào)等待,所以當(dāng)同時(shí)創(chuàng)建了10個(gè)線程之后,for循環(huán)就會(huì)阻塞,等待有線程結(jié)束之后會(huì)增加一個(gè)信號(hào)才繼續(xù)執(zhí)行,如此就形成了對(duì)并發(fā)的控制,如上就是一個(gè)并發(fā)數(shù)為10的一個(gè)線程隊(duì)列。

pthread_mutex_t

  1. 如果互斥鎖類型為 PTHREAD_MUTEX_NORMAL,則不提供死鎖檢測(cè)。嘗試重新鎖定互斥鎖會(huì)導(dǎo)致死鎖。如果某個(gè)線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或未鎖定,則將產(chǎn)生不確定的行為。

  2. 如果互斥鎖類型為 PTHREAD_MUTEX_ERRORCHECK,則會(huì)提供錯(cuò)誤檢查。如果某個(gè)線程嘗試重新鎖定的互斥鎖已經(jīng)由該線程鎖定,則將返回錯(cuò)誤。如果某個(gè)線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯(cuò)誤。

  3. 如果互斥鎖類型為 PTHREAD_MUTEX_RECURSIVE,則該互斥鎖會(huì)保留鎖定計(jì)數(shù)這一概念。線程首次成功獲取互斥鎖時(shí),鎖定計(jì)數(shù)會(huì)設(shè)置為 1。線程每重新鎖定該互斥鎖一次,鎖定計(jì)數(shù)就增加 1。線程每解除鎖定該互斥鎖一次,鎖定計(jì)數(shù)就減小 1。 鎖定計(jì)數(shù)達(dá)到 0 時(shí),該互斥鎖即可供其他線程獲取。如果某個(gè)線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯(cuò)誤。

  4. 如果互斥鎖類型是 PTHREAD_MUTEX_DEFAULT,則嘗試以遞歸方式鎖定該互斥鎖將產(chǎn)生不確定的行為。對(duì)于不是由調(diào)用線程鎖定的互斥鎖,如果嘗試解除對(duì)它的鎖定,則會(huì)產(chǎn)生不確定的行為。如果嘗試解除鎖定尚未鎖定的互斥鎖,則會(huì)產(chǎn)生不確定的行為。

//主線程中
TestObj *obj = [[TestObj alloc] init];

__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [obj method1];
    sleep(5);
    pthread_mutex_unlock(&mutex);
});

//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    pthread_mutex_lock(&mutex);
    [obj method2];
    pthread_mutex_unlock(&mutex);
});

@synchronized

//主線程中
TestObj *obj = [[TestObj alloc] init];

//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(obj){
        [obj method1];
        sleep(10);
    }
});

//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    @synchronized(obj){
        [obj method2];
    }
});

NSLock

//主線程中
TestObj *obj = [[TestObj alloc] init];
NSLock *lock = [[NSLock alloc] init];

//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    [obj method1];
    sleep(10);
    [lock unlock];
});

//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);//以保證讓線程2的代碼后執(zhí)行
    [lock lock];
    [obj method2];
    [lock unlock];
});

NSLock是Cocoa提供給我們最基本的鎖對(duì)象,這也是我們經(jīng)常所使用的,除lock和unlock方法外,NSLock還提供了tryLock和lockBeforeDate:兩個(gè)方法,前一個(gè)方法會(huì)嘗試加鎖,如果鎖不可用(已經(jīng)被鎖住),剛并不會(huì)阻塞線程,并返回NO。lockBeforeDate:方法會(huì)在所指定Date之前嘗試加鎖,如果在指定時(shí)間之前都不能加鎖,則返回NO。

NSRecursiveLock

NSRecursiveLock實(shí)際上定義的是一個(gè)遞歸鎖,這個(gè)鎖可以被同一線程多次請(qǐng)求,而不會(huì)引起死鎖。這主要是用在循環(huán)或遞歸操作中。我們先來(lái)看一個(gè)示例:

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
    static void (^RecursiveMethod)(int);
 
    RecursiveMethod = ^(int value) {
 
        [lock lock];
        if (value > 0) {
 
            NSLog(@"value = %d", value);
            sleep(2);
            RecursiveMethod(value - 1);
        }
        [lock unlock];
    };
 
    RecursiveMethod(5);
});
 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
    sleep(2);
    BOOL flag = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
    if (flag) {
        NSLog(@"lock before date");
 
        [lock unlock];
    } else {
        NSLog(@"fail to lock before date");
    }
});

NSRecursiveLock除了實(shí)現(xiàn)NSLocking協(xié)議的方法外,還提供了兩個(gè)方法,分別如下:

// 在給定的時(shí)間之前去嘗試請(qǐng)求一個(gè)鎖
- (BOOL)lockBeforeDate:(NSDate *)limit
 
// 嘗試去請(qǐng)求一個(gè)鎖,并會(huì)立即返回一個(gè)布爾值,表示嘗試是否成功
- (BOOL)tryLock

另外,NSRecursiveLock還聲明了一個(gè)name屬性,如下:

@property(copy) NSString *name

我們可以使用這個(gè)字符串來(lái)標(biāo)識(shí)一個(gè)鎖。Cocoa也會(huì)使用這個(gè)name作為錯(cuò)誤描述信息的一部分。

NSCondition

使用NSCondition,實(shí)現(xiàn)多線程的同步,即,可實(shí)現(xiàn)生產(chǎn)者消費(fèi)者問題。

基本思路是,首先要?jiǎng)?chuàng)建公用的NSCondition實(shí)例。然后:

消費(fèi)者取得鎖,取產(chǎn)品,如果沒有,則wait,這時(shí)會(huì)釋放鎖,直到有線程喚醒它去消費(fèi)產(chǎn)品;

生產(chǎn)者制造產(chǎn)品,首先也是要取得鎖,然后生產(chǎn),再發(fā)signal,這樣可喚醒wait的消費(fèi)者。

- (IBAction)conditionTest:(id)sender
{
    NSLog(@"begin condition works!");
    products = [[NSMutableArray alloc] init];
    condition = [[NSCondition alloc] init];
     
    [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];
}
 
- (void)createConsumenr
{
    [condition lock];
    while ([products count] == 0) {
        NSLog(@"wait for products");
        [condition wait];
    }
    [products removeObjectAtIndex:0];
    NSLog(@"comsume a product");
    [condition unlock];
}
 
- (void)createProducter
{
    [condition lock];
    [products addObject:[[NSObject alloc] init]];
    NSLog(@"produce a product");
    [condition signal];
    [condition unlock];
}

NSConditionLock

//主線程中
NSConditionLock *theLock = [[NSConditionLock alloc] init];

//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i=0;i<=2;i++)
    {
        [theLock lock];
        NSLog(@"thread1:%d",i);
        sleep(2);
        [theLock unlockWithCondition:i];
    }
});

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

NSDistributedLock 分布式鎖

?著作權(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ī)制來(lái)訪問共享資源。 一、 互斥鎖 互斥鎖的意思是某一時(shí)刻只允許一個(gè)線程訪問某一資源。為了保證...
    doudo閱讀 819評(píng)論 0 5
  • 鎖是一種同步機(jī)制,用于多線程環(huán)境中對(duì)資源訪問的限制iOS中常見鎖的性能對(duì)比圖(摘自:ibireme): iOS鎖的...
    LiLS閱讀 1,628評(píng)論 0 6
  • 前言 一塊資源可能會(huì)被多個(gè)線程共享,也就是多個(gè)線程可能會(huì)訪問同一塊資源,比如多個(gè)線程訪問同一個(gè)對(duì)象、同一個(gè)變量、同...
    WQ_UESTC閱讀 998評(píng)論 0 5
  • 前言 iOS開發(fā)中由于各種第三方庫(kù)的高度封裝,對(duì)鎖的使用很少,剛好之前面試中被問到的關(guān)于并發(fā)編程鎖的問題,都是一知...
    喵渣渣閱讀 3,866評(píng)論 0 33
  • iOS線程安全的鎖與性能對(duì)比 一、鎖的基本使用方法 1.1、@synchronized 這是我們最熟悉的枷鎖方式,...
    Jacky_Yang閱讀 2,374評(píng)論 0 17

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