iOS Sharing #03 | 2019-04-06

目錄

一、atomic關鍵字內部使用的是什麼鎖?

二、串行同步、串行異步、併發同步、併發異步各自會開幾條線程?

三、爲何須要在主線程更新UI?

四、iOS中如何用多線程實現多讀單寫?

五、iOS多線程中有多少種方式能夠作到等待前面線程執行完畢再執行後面的線程?


一、atomic關鍵字內部使用的是什麼鎖?

答:

首先了解一些基本概念:git

  • 臨界區:指的是一塊對公共資源進行訪問的代碼,並不是一種機制或是算法。github

  • 自旋鎖:是用於多線程同步的一種鎖,線程反覆檢查鎖變量是否可用。因爲線程在這一過程當中保持執行,所以是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,直至顯式釋放自旋鎖。 自旋鎖避免了進程上下文的調度開銷,所以對於線程只會阻塞很短期的場合是有效的。算法

  • 互斥鎖(Mutex):是一種用於多線程編程中,防止兩條線程同時對同一公共資源(好比全局變量)進行讀寫的機制。該目的經過將代碼切片成一個一個的臨界區而達成。編程

  • 讀寫鎖:是計算機程序的併發控制的一種同步機制,也稱「共享-互斥鎖」、多讀者-單寫者鎖) 用於解決多線程對公共資源讀寫問題。讀操做可併發重入,寫操做是互斥的。 讀寫鎖一般用互斥鎖、條件變量、信號量實現。安全

  • 信號量(semaphore):是一種更高級的同步機制,互斥鎖能夠說是semaphore在僅取值0/1時的特例。信號量能夠有更多的取值空間,用來實現更加複雜的同步,而不僅僅是線程間互斥。bash

  • 條件鎖:就是條件變量,當進程的某些資源要求不知足時就進入休眠,也就是鎖住了。當資源被分配到了,條件鎖打開,進程繼續運行。多線程

  • 死鎖:指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去,這些永遠在互相等待的進程稱爲死鎖進程。併發

  • 輪詢(Polling):一種CPU決策如何提供周邊設備服務的方式,又稱「程控輸出入」。輪詢法的概念是,由CPU定時發出詢問,依序詢問每個周邊設備是否須要其服務,有即給予服務,服務結束後再問下一個周邊,接着不斷周而復始。app

鎖的類型:異步

  • 互斥鎖

    • NSLock
    • pthread_mutex
    • pthread_mutex(recursive)遞歸鎖
    • @synchronized
  • 自旋鎖

    • OSSpinLock
    • os_unfair_lock
  • 讀寫鎖

    • pthread_rwlock
  • 遞歸鎖

    • NSRecursiveLock

    • pthread_mutex(recursive)(見上)

  • 條件鎖

    • NSCondition
    • NSConditionLock
  • 信號量

    • dispatch_semaphore

time

//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
複製代碼

atomic

atomic使用的是自旋鎖,主要用於賦值操做等輕量操做(散列表,引用計數,弱引用指針賦值),而互斥鎖通常都是鎖線程,好比單例。


二、串行同步、串行異步、併發同步、併發異步各自會開幾條線程?

答:

首先了解一些基本概念:

  • 同步:只能在當前線程中執行任務,不具有開啓新線程的能力
  • 異步:能夠在新的線程中執行任務,具有開啓新線程的能力
  • 串行:一個任務執行完畢後,再執行下一個任務
  • 併發:多個任務併發(同時)執行

因此:

  • 串行同步不開新線程
  • 串行異步開啓一條新線程
  • 併發同步不開新線程
  • 併發異步開啓多條

三、爲何須要在主線程更新UI?

答:
  • 安全

在非主線程中更新UI就會有多個線程同時操做一個控件的可能,形成最後更新的結果不符合預期

  • 效率

多線程自己就是爲了併發處理以達到高效的目的,可是刷新UI使用併發會形成安全問題,要解決上面的安全問題,那就須要給控件加鎖,可是加鎖必然會形成額外的開銷,同時開新的線程自己就有必定的開銷,因此不如直接在主線程中執行更新操做。


四、iOS中如何用多線程實現多讀單寫?

答:
@interface CustomDictionary ()

//多線程須要訪問的數據量
@property (nonatomic, strong) NSMutableDictionary *dataDic;

@end

//模擬場景,容許多個線程同時訪問字典,可是隻有一個線程能夠寫字典
@implementation CustomDictionary {
    //定義一個併發隊列
    dispatch_queue_t _concurrent_queue;

}

- (instancetype)init {
    if (self = [super init]) {
        _concurrent_queue = dispatch_queue_create("com.mf.read_write_queue", DISPATCH_QUEUE_CONCURRENT);
        _dataDic = @{}.mutableCopy;
    }
    
    return self;
}

// 讀取數據,併發操做
- (id)objectForKey:(NSString *)key {
    __block id obj;
    //同步讀取數據
    dispatch_sync(_concurrent_queue, ^{
        obj = [self.dataDic objectForKey:key];
    });
    
    return obj;
    
}

// 寫入數據,異步柵欄
- (void)setObject:(id)obj forKey:(NSString *)key {
    //異步柵欄調用設置數據
    dispatch_barrier_async(_concurrent_queue, ^{
        [self.dataDic setObject:obj forKey:key];
    });
}

@end

複製代碼

五、iOS多線程中有多少種方式能夠作到等待前面線程執行完畢再執行後面的線程?

答:
  • barrier
dispatch_queue_t queue = dispatch_queue_create("com.mf.barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"1");
});
    
dispatch_async(queue, ^{
    NSLog(@"2");
});
    
dispatch_barrier_async(queue, ^{
    NSLog(@"等待任務1,2上面執行完畢");
});
    
dispatch_async(queue, ^{
    NSLog(@"3");
});
    
dispatch_async(queue, ^{
    NSLog(@"4");
});
複製代碼
  • group notify
// 全局變量group
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 進入組(進入組和離開組必須成對出現, 不然會形成死鎖)
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
    NSLog(@"1");
    //離開組
    dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
    NSLog(@"2");
    dispatch_group_leave(group);
});

dispatch_group_notify(group, queue, ^{  // 監聽組裏全部線程完成的狀況
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務1,2已完成");
    });    
});
複製代碼
  • NSOperationQueue Dependency
//建立隊列
    NSOperationQueue *queue=[[NSOperationQueue alloc] init];
    //建立操做
    NSBlockOperation *operation1=[NSBlockOperation blockOperationWithBlock:^(){
        NSLog(@"執行第1次操做,線程:%@",[NSThread currentThread]);
    }];
    NSBlockOperation *operation2=[NSBlockOperation blockOperationWithBlock:^(){
        NSLog(@"執行第2次操做,線程:%@",[NSThread currentThread]);
    }];
    NSBlockOperation *operation3=[NSBlockOperation blockOperationWithBlock:^(){
        NSLog(@"執行第3次操做,線程:%@",[NSThread currentThread]);
    }];
    //添加依賴
    [operation1 addDependency:operation2];
    [operation2 addDependency:operation3];
    //將操做添加到隊列中去
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
複製代碼
  • semaphore
/* 好比說咱們須要請求三張元素圖,拼合成一張海報。咱們須要先對元素圖進行請求然後才能合成海報,這就造成了依賴關係。咱們經過semaphore限制資源數爲3,供請求元素圖使用,待請求完成後,釋放信號量,便能走到合成的耗時操做。 */

//建立信號量,參數:信號量的初值,若是小於0則會返回NULL
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
    
//元素圖1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    //等待下降信號量
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"請求第1張元素圖");
    sleep(1);
    NSLog(@"第1張元素圖Get");
    //提升信號量
    dispatch_semaphore_signal(semaphore);
});
    
//元素圖2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"請求第2張元素圖");
    sleep(1);
    NSLog(@"第2張元素圖Get");
    dispatch_semaphore_signal(semaphore);
});
    
//元素圖3
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"請求第3張元素圖");
    sleep(1);
    NSLog(@"第3張元素圖Get");
    dispatch_semaphore_signal(semaphore);
});
    
//合成海報
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"合成海報");
    sleep(1);
    NSLog(@"海報Get");
    dispatch_semaphore_signal(semaphore);
});
複製代碼

注意:
正常的使用順序是先下降而後再提升,這兩個函數一般成對使用。


倉庫

本篇相關代碼


聯繫方式

郵箱: adrenine@163.com

郵箱: holaux@gmail.com

郵箱: ledahapple@icloud.com

相關文章
相關標籤/搜索