MJiOS底層筆記--多線程

本文屬筆記性質,主要針對本身理解不太透徹的地方進行記錄。

推薦系統直接學習小碼哥iOS底層原理班---MJ老師的課確實不錯,強推一波。ios


常見的多線程方案


線程的開闢與阻塞機制

  • 並行和串行主要影響:任務的執行方式

並行:多個任務併發(同時)執行 串行:一個任務執行完畢後,再執行下一個任務數組

  • 同步和異步主要影響:能不能開啓新的線程

同步:在當前線程中執行任務,不具有開啓新線程的能力 異步:在新的線程中執行任務,具有開啓新線程的能力安全

  • 會開闢新線程的兩種狀況bash

    • 並行隊列+異步任務 = 多條新線程
    • 自定義串行多列+異步任務 = 一條新線程
  • 其他狀況、所有將會置於當前線程/主線程(主隊列任務)下執行。多線程

GCD


多線程的安全隱患

資源搶奪

1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源併發

對共用資源的 讀寫進行枷鎖操做


自旋鎖

等待鎖的線程會處於忙等(busy-wait)狀態,一直佔用着CPU資源異步

OSSpinLock

#import <libkern/OSAtomic.h>

@interface OSSpinLockDemo()
@property (assign, nonatomic) OSSpinLock lock;
@end

@implementation OSSpinLockDemo
- (instancetype)init
{
    if (self = [super init]) {
        self.lock = OS_SPINLOCK_INIT;
    }
    return self;
}


- (void)test
{
    OSSpinLockLock(&_lock);
    
    //同步操做
    
    OSSpinLockUnlock(&_lock);
}
@end

複製代碼

優先級反轉

OSSpinLock目前已經再也不安全,可能會出現優先級反轉問題async

若是等待鎖的線程優先級較高,它會一直佔用着CPU資源,優先級低的線程就有可能得不到資源致使沒法釋放鎖。性能


互斥鎖(普通鎖)

等待os_unfair_lock鎖的線程會處於休眠狀態,並不是忙等學習

os_unfair_lock

os_unfair_lock用於取代不安全的OSSpinLock ,從iOS10開始才支持

<os/lock.h>

@interface OSUnfairLockDemo()
@property (assign, nonatomic) os_unfair_lock lock;
@end

@implementation OSUnfairLockDemo

- (instancetype)init
{
    if (self = [super init]) {
        self.lock = OS_UNFAIR_LOCK_INIT;
    }
    return self;
}


- (void)__saleTicket
{
    os_unfair_lock_lock(&_lock);
    
    //耗時操做
    
    os_unfair_lock_unlock(&_lock);
}

@end
複製代碼

pthread_mutex - PTHREAD_MUTEX_DEFAULT&&PTHREAD_MUTEX_NORMAL

PTHREAD_MUTEX_DEFAULT或PTHREAD_MUTEX_NORMAL下的 pthread_mutex 會產生互斥效果

NSLock

NSLock是對mutex普通鎖的封裝


遞歸鎖

容許同一個線程對一把鎖進行重複加鎖

pthread_mutex - PTHREAD_MUTEX_RECURSIVE

PTHREAD_MUTEX_RECURSIVE 下的 pthread_mutex 會產生遞歸效果

NSRecursiveLock

NSRecursiveLock也是對mutex遞歸鎖的封裝,API跟NSLock基本一致

@synchronized

@synchronized是對mutex遞歸鎖的封裝

@synchronized(obj)內部會生成obj對應的遞歸鎖並存在hash表中,而後進行加鎖、解鎖操做。性能最差

-(void)test {
    @synchronized(self) {
        NSLog(@"2");
        [self tess];
        sleep(5);
    }
}
複製代碼

條件鎖

可讓一個線程在加鎖途中等待另外一個線程完成某個動做後繼續加鎖執行

相似消費者在商場等着商家調貨,而後繼續購買。

在上鎖狀態下能夠暫時將鎖放開,休眠並等待某個條件

當其餘線程對條件發送信號,喚醒繼續加鎖並執行

#### pthread_mutex - pthread_cond_t

@interface MutexDemo3()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation MutexDemo3

- (instancetype)init
{
    if (self = [super init]) {
        // 初始化屬性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        // 初始化鎖
        pthread_mutex_init(&_mutex, &attr);
        // 銷燬屬性
        pthread_mutexattr_destroy(&attr);
        
        // 初始化條件
        pthread_cond_init(&_cond, NULL);
        
        self.data = [NSMutableArray array];
    }
    return self;
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}


// 線程1
// 刪除數組中的元素
- (void)__remove
{
    pthread_mutex_lock(&_mutex);
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待喚醒
        pthread_cond_wait(&_cond, &_mutex);
    }
    
    [self.data removeLastObject];
    NSLog(@"刪除了元素");
    
    pthread_mutex_unlock(&_mutex);
}

// 線程2
// 往數組中添加元素
- (void)__add
{
    pthread_mutex_lock(&_mutex);
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    
    // 發送喚醒信號
    pthread_cond_signal(&_cond);

    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
}

@end

複製代碼

NSCondition

NSCondition是對mutex和cond的封裝

NSConditionLock

NSConditionLock是對NSCondition的進一步封裝,能夠設置具體的條件值

- (instancetype)init
{
    if (self = [super init]) {
        self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
    }
    return self;
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one
{
    [self.conditionLock lock];
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two
{
    [self.conditionLock lockWhenCondition:2];
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}
複製代碼

dispatch_semaphore

信號量的初始值爲1,表明同時只容許1條線程訪問資源,保證線程同步

也能夠控制線程的最大併發數

- (void)test
{
    // 若是信號量的值 > 0,就讓信號量的值減1,而後繼續往下執行代碼
    // 若是信號量的值 <= 0,就會休眠等待,直到信號量的值變成>0,就讓信號量的值減1,而後繼續往下執行代碼
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    
    sleep(2);
    NSLog(@"test - %@", [NSThread currentThread]);
    
    // 讓信號量的值+1
    dispatch_semaphore_signal(self.semaphore);
}
複製代碼

性能排序

  1. os_unfair_lock
  2. OSSpinLock
  3. dispatch_semaphore //推薦
  4. pthread_mutex //推薦
  5. dispatch_queue(DISPATCH_QUEUE_SERIAL)
  6. NSLock
  7. NSCondition
  8. pthread_mutex(recursive)
  9. NSRecursiveLock 10 .NSConditionLock 11 .@synchronized

自旋鎖、互斥鎖比較

什麼狀況使用自旋鎖比較划算?

  1. 預計線程等待鎖的時間很短
  2. 加鎖的代碼(臨界區)常常被調用,但競爭狀況不多發生
  3. CPU資源不緊張
  4. 多核處理器

什麼狀況使用互斥鎖比較划算?

  1. 預計線程等待鎖的時間較長
  2. 單核處理器
  3. 臨界區有IO操做
  4. 臨界區代碼複雜或者循環量大
  5. 臨界區競爭很是激烈

atomic

atomic用於保證屬性setter、getter的原子性操做,至關於在getter和setter內部加了線程同步的鎖

能夠參考源碼objc4的objc-accessors


iOS中的讀寫安全方案

讀寫鎖

dispatch_barrier_async

@interface ViewController ()
@property (strong, nonatomic) dispatch_queue_t queue;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//    queue.maxConcurrentOperationCount = 5;
    
//    dispatch_semaphore_create(5);
    
    self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_barrier_async(self.queue, ^{
            [self write];
        });
    }
}


- (void)read {
    sleep(1);
    NSLog(@"read");
}

- (void)write
{
    sleep(1);
    NSLog(@"write");
}

@end
複製代碼

pthread_rwlock_t

@interface ViewController ()
@property (assign, nonatomic) pthread_rwlock_t lock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化鎖
    pthread_rwlock_init(&_lock, NULL);
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            [self read];
        });
        dispatch_async(queue, ^{
            [self write];
        });
    }
}


- (void)read {
    pthread_rwlock_rdlock(&_lock);
    
    sleep(1);
    NSLog(@"%s", __func__);
    
    pthread_rwlock_unlock(&_lock);
}

- (void)write
{
    pthread_rwlock_wrlock(&_lock);
    
    sleep(1);
    NSLog(@"%s", __func__);
    
    pthread_rwlock_unlock(&_lock);
}

- (void)dealloc
{
    pthread_rwlock_destroy(&_lock);
}


@end
複製代碼
相關文章
相關標籤/搜索