iOS 中常見的幾種鎖-代碼示例

iOS 中常見的幾種鎖介紹-示例

經常使用的各種鎖性能比較

經常使用的各種鎖性能比較

文中Demo 均實如今 XWInterviewDemos

1. iOS中的互斥鎖

在編程中,引入對象互斥鎖的概念,來保證共享數據操做的完整性。每一個對象都對應於一個可稱爲「互斥鎖」的標記,這個標記用來保證在任一時刻,只能有一個線程訪問對象。ios

1.1 @synchronized (self)

- (void)lock1 {
    @synchronized (self) {
        // 加鎖操做
    }
}
複製代碼

1.2 NSLock

- (void)lock2 {
    NSLock *xwlock = [[NSLock alloc] init];
    XWLogBlock logBlock = ^ (NSArray *array) {
        [xwlock lock];
        for (id obj in array) {
            NSLog(@"%@",obj);
        }
        [xwlock unlock];
    };
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSArray *array = @[@1,@2,@3];
        logBlock(array);
    });
}
複製代碼

1.3 pthread

pthread除了建立互斥鎖,還能夠建立遞歸鎖、讀寫鎖、once等鎖git

__block pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"+++++ 線程1 start");
        pthread_mutex_lock(&mutex);
        sleep(2);
        pthread_mutex_unlock(&mutex);
        NSLog(@"+++++ 線程1 end");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"----- 線程2 start");
        pthread_mutex_lock(&mutex);
        sleep(3);
        pthread_mutex_unlock(&mutex);
        NSLog(@"----- 線程2 end");
    });
}
複製代碼

2. iOS中的遞歸鎖

同一個線程能夠屢次加鎖,不會形成死鎖github

死鎖->
- (void)lock5 {
    NSLock *commonLock = [[NSLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^XWRecursiveBlock)(int);
        
        XWRecursiveBlock = ^(int  value) {
            [commonLock lock];
            if (value > 0) {
                NSLog(@"加鎖層數: %d",value);
                sleep(1);
                XWRecursiveBlock(--value);
            }
            NSLog(@"程序退出!");
            [commonLock unlock];
        };
        
        XWRecursiveBlock(3);
    });
}
複製代碼
<-死鎖

2.1 NSRecursiveLock

- (void)lock4 {
    NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^XWRecursiveBlock)(int);
        
        XWRecursiveBlock = ^(int  value) {
            [recursiveLock lock];
            if (value > 0) {
                NSLog(@"加鎖層數: %d",value);
                sleep(1);
                XWRecursiveBlock(--value);
            }
            NSLog(@"程序退出!");
            [recursiveLock unlock];
        };
        
        XWRecursiveBlock(3);
    });
}
複製代碼

2.2 pthread

- (void)lock6 {
    __block pthread_mutex_t recursiveMutex;
    pthread_mutexattr_t recursiveMutexattr;
    
    pthread_mutexattr_init(&recursiveMutexattr);
    pthread_mutexattr_settype(&recursiveMutexattr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&recursiveMutex, &recursiveMutexattr);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^XWRecursiveBlock)(int);
        
        XWRecursiveBlock = ^(int  value) {
            pthread_mutex_lock(&recursiveMutex);
            if (value > 0) {
                NSLog(@"加鎖層數: %d",value);
                sleep(1);
                XWRecursiveBlock(--value);
            }
            NSLog(@"程序退出!");
            pthread_mutex_unlock(&recursiveMutex);
        };
        
        XWRecursiveBlock(3);
    });
}
複製代碼

3. 信號量

信號量(Semaphore),有時被稱爲信號燈,是在多線程環境下使用的一種設施,是能夠用來保證兩個或多個關鍵代碼段不被併發調用。在進入一個關鍵代碼段以前,線程必須獲取一個信號量;一旦該關鍵代碼段完成了,那麼該線程必須釋放信號量。其它想進入該關鍵代碼段的線程必須等待直到第一個線程釋放信號量objective-c

3.1 dispatch_semaphore_t

實現 GCD 下同步
- (void)lock7 {
//    dispatch_semaphore_create //建立一個信號量 semaphore
//    dispatch_semaphore_signal //發送一個信號 信號量+1
//    dispatch_semaphore_wait   // 等待信號 信號量-1
    
    /// 需求: 異步線程的兩個操做同步執行
    
    dispatch_semaphore_t semaphone = dispatch_semaphore_create(0);
    NSLog(@"start");
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"async .... ");
        sleep(5);
        /// 線程資源 + 1
        dispatch_semaphore_signal(semaphone);//信號量+1
    });
    /// 當前線程資源數量爲 0 ,等待激活
    dispatch_semaphore_wait(semaphone, DISPATCH_TIME_FOREVER);
    NSLog(@"end");
}
複製代碼

3.2 pthread

- (void)lock8 {
    __block pthread_mutex_t semaphore = PTHREAD_MUTEX_INITIALIZER;
    __block pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    NSLog(@"start");
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        pthread_mutex_lock(&semaphore);
        NSLog(@"async...");
        sleep(5);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&semaphore);
    });
    
    pthread_cond_wait(&cond, &semaphore);
    NSLog(@"end");
}
複製代碼

4. 條件鎖

4.1 NSCondition

NSCondition 的對象其實是做爲一個鎖和線程檢查器,鎖主要是爲了檢測條件時保護數據源,執行條件引起的任務。線程檢查器主要是根據條件決定是否繼續運行線程,即線程是否被阻塞。編程

  • NSCondition一樣實現了NSLocking協議,因此它和NSLock同樣,也有NSLocking協議的lock和unlock方法,能夠當作NSLock來使用解決線程同步問題,用法徹底同樣。
- (NSMutableArray *)removeLastImage:(NSMutableArray *)images {
    if (images.count > 0) {
        NSCondition *condition = [[NSCondition alloc] init];
        [condition lock];
        [images removeLastObject];
        [condition unlock];
        NSLog(@"removeLastImage %@",images);
        return images;
    }else{
        return NULL;
    }
}
複製代碼
  • 同時,NSCondition提供更高級的用法。wait和signal,和條件信號量相似。數組

  • NSCondition和NSLock、@synchronized等是不一樣的是,NSCondition能夠給每一個線程分別加鎖,加鎖後不影響其餘線程進入臨界區。這是很是強大。安全

- (void)lock10 {
    self.conditionArray = [NSMutableArray array];
    self.xwCondition = [[NSCondition alloc] init];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.xwCondition lock];
        if (self.conditionArray.count == 0) {
            NSLog(@"等待制做數組");
            [self.xwCondition wait];
        }
        id obj = self.conditionArray[0];
        NSLog(@"獲取對象進行操做:%@",obj);
        [self.xwCondition unlock];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.xwCondition lock];
        id obj = @"極客學偉";
        [self.conditionArray addObject:obj];
        NSLog(@"建立了一個對象:%@",obj);
        [self.xwCondition signal];
        [self.xwCondition unlock];
    });
}
複製代碼

4.2 NSConditionLock

- (void)lock11 {
    NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
    NSMutableArray *arrayM = [NSMutableArray array];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [conditionLock lock];
        
        for (int i = 0; i < 6; i++) {
            [arrayM addObject:@(i)];
            NSLog(@"異步下載第 %d 張圖片",i);
            sleep(1);
            if (arrayM.count == 4) {
                [conditionLock unlockWithCondition:4];
            }
        }
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [conditionLock lockWhenCondition:4];
        NSLog(@"已經獲取到4張圖片->主線程渲染");
        [conditionLock unlock];
    });
}
複製代碼

4.2 pthread POSIX Conditions

- (void)lock12 {
    __block pthread_mutex_t mutex;
    __block pthread_cond_t condition;
    
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&condition, NULL);
    
    NSMutableArray *arrayM = [NSMutableArray array];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        pthread_mutex_lock(&mutex);
        
        for (int i = 0; i < 6; i++) {
            [arrayM addObject:@(i)];
            NSLog(@"異步下載第 %d 張圖片",i);
            sleep(1);
            if (arrayM.count == 4) {
                pthread_cond_signal(&condition);
            }
        }
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        pthread_cond_wait(&condition, &mutex);
        NSLog(@"已經獲取到4張圖片->主線程渲染");
        pthread_mutex_unlock(&mutex);
    });
}
複製代碼

5. 讀寫鎖

讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分紅讀者和寫者,讀者只對共享資源進行讀訪問,寫者則須要對共享資源進行寫操做。這種鎖相對於自旋鎖而言,能提升併發性,由於在多處理器系統中,它容許同時有多個讀者來訪問共享資源,最大可能的讀者數爲實際的邏輯CPU數。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數相關),但不能同時既有讀者又有寫者。多線程

5.1 dispatch_barrier_async / dispatch_barrier_sync

有一個需求,如圖:併發

barrierdemo

任務1,2,3 均執行完畢執行任務0,而後執行任務4,5,6.
- (void)lock13 {
    dispatch_queue_t queue = dispatch_queue_create("com.qiuxuewei.brrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"任務1 -- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務2 -- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務3 -- %@",[NSThread currentThread]);
    });
    dispatch_barrier_sync(queue, ^{
        NSLog(@"任務0 -- %@",[NSThread currentThread]);
        for (int i = 0; i < 100; i++) {
            if (i % 30 == 0) {
                NSLog(@"任務0 --- log:%d -- %@",i,[NSThread currentThread]);
            }
        }
    });
    NSLog(@"dispatch_barrier_sync  down!!!");
    dispatch_async(queue, ^{
        NSLog(@"任務4 -- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務5 -- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務6 -- %@",[NSThread currentThread]);
    });
}
複製代碼
dispatch_barrier_asyncdispatch_barrier_sync 的異同
共同點
  • 等待它前面的執行完才執行本身的任務
  • 等待本身任務執行結束才執行後面的任務
不一樣點
  • dispatch_barrier_async 將本身的任務插入到隊列以後會繼續將後面的操做插入到隊列,按照規則先執行前面隊列的任務,等本身隊列執行完畢,再執行後面隊列的任務
  • dispatch_barrier_sync 將本身的任務插入到隊列以後,先等待本身的任務執行完畢纔會執行後面操做插入到隊列,再執行後面隊列的任務。

5.2 pthread

- (void)lock14 {
    __block pthread_rwlock_t rwlock;
    pthread_rwlock_init(&rwlock, NULL);
    __block NSMutableArray *arrayM = [NSMutableArray array];
    
    XWBlock writeBlock = ^ (NSString *str) {
        NSLog(@"開啓寫操做");
        pthread_rwlock_wrlock(&rwlock);
        [arrayM addObject:str];
        sleep(2);
        pthread_rwlock_unlock(&rwlock);
    };
    
    XWVoidBlock readBlock = ^ {
        NSLog(@"開啓讀操做");
        pthread_rwlock_rdlock(&rwlock);
        sleep(1);
        NSLog(@"讀取數據:%@",arrayM);
        pthread_rwlock_unlock(&rwlock);
    };
    
    for (int i = 0; i < 5; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            writeBlock([NSString stringWithFormat:@"%d",i]);
        });
    }
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            readBlock();
        });
    }
}
複製代碼

6.自旋鎖

bool lock = false; // 一開始沒有鎖上,任何線程均可以申請鎖  
do {  
    while(lock); // 若是 lock 爲 true 就一直死循環,至關於申請鎖
    lock = true; // 掛上鎖,這樣別的線程就沒法得到鎖
        Critical section  // 臨界區
    lock = false; // 至關於釋放鎖,這樣別的線程能夠進入臨界區
        Reminder section // 不須要鎖保護的代碼        
}

複製代碼

6.1 OSSpinLock

YYKit做者的文章 再也不安全的 OSSpinLock有說到這個自旋鎖存在優先級反轉的問題。異步

6.2 os_unfair_lock

自旋鎖已經再也不安全,而後蘋果又整出來個 os_unfair_lock_t ,這個鎖解決了優先級反轉的問題。

os_unfair_lock_t unfairLock;
    unfairLock = &(OS_UNFAIR_LOCK_INIT);
    os_unfair_lock_lock(unfairLock);
    os_unfair_lock_unlock(unfairLock);
複製代碼

7. property - atomic / nonatomic

atomic 修飾的對象,系統會保證在其自動生成的 getter/setter 方法中的操做是完整的,不受其餘線程的影響。例如 A 線程在執行 getter 方法時,B線程執行了 setter 方法,此時 A 線程依然會獲得一個完整無損的對象。

atomic

默認修飾符 會保證CPU能在別的線程訪問這個屬性以前先執行完當前操做 讀寫速度慢 線程不安全 - 若是有另外一個線程 D 同時在調[name release],那可能就會crash,由於 release 不受 getter/setter 操做的限制。也就是說,這個屬性只能說是讀/寫安全的,但並非線程安全的,由於別的線程還能進行讀寫以外的其餘操做。線程安全須要開發者本身來保證。

nonatomic

不默認 速度更快 線程不安全 若是兩個線程同時訪問會出現不可預料的結果。

8. Once 原子操做

8.1 GCD

- (id)lock15 {
    static id shareInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (!shareInstance) {
            shareInstance = [[NSObject alloc] init];
        }
    });
    return shareInstance;
}
複製代碼

8.2 pthread

- (void)lock16 {
    pthread_once_t once = PTHREAD_ONCE_INIT;
    pthread_once(&once, lock16Func);
}
void lock16Func() {
     static id shareInstance;
    shareInstance = [[NSObject alloc] init];
}
複製代碼
相關文章
相關標籤/搜索