IOS中的線程同步方案

線程同步方案

  • OSSpinLock 自旋鎖
  • os_unfair_lock
  • pthread_mutex
  • dispatch_semaphore 信號(hào)量
  • dispatch_queue(DISPATCH_QUEUE_SERIAL) 串行隊(duì)列
  • NSLock
  • NSRecursiveLock
  • NSCondition
  • NSConditionLock 條件鎖
  • @synchronized
賣票方法
#import "ViewController.h"
#import <libkern/OSAtomic.h> //導(dǎo)入頭文件 自旋鎖
@interface ViewController ()
@property (assign, nonatomic) OSSpinLock lock;
@end

/**
 賣票演示
 */
- (void)ticketTest
{
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}


/**
 賣1張票
 */
- (void)saleTicket
{
        // 加鎖
    OSSpinLockLock(&_lock);

    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"還剩%d張票 - %@", oldTicketsCount, [NSThread currentThread]);
    
    // 解鎖
    OSSpinLockUnlock(&_lock);

//嘗試加鎖 更加安全
 // if (OSSpinLockTry(&_lock)) {
//        int oldTicketsCount = self.ticketsCount;
//        sleep(.2);
//        oldTicketsCount--;
//        self.ticketsCount = oldTicketsCount;
//        NSLog(@"還剩%d張票 - %@", oldTicketsCount, [NSThread currentThread]);
//
//        OSSpinLockUnlock(&_lock);
//    }
    
}


2018-09-12 09:49:28.048945+0800 Interview04-線程同步[986:58920] 還剩14張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.049229+0800 Interview04-線程同步[986:58920] 還剩13張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.049457+0800 Interview04-線程同步[986:58920] 還剩12張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.050802+0800 Interview04-線程同步[986:58920] 還剩11張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.050978+0800 Interview04-線程同步[986:58920] 還剩10張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.051562+0800 Interview04-線程同步[986:58917] 還剩9張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.051847+0800 Interview04-線程同步[986:58917] 還剩8張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.052481+0800 Interview04-線程同步[986:58917] 還剩7張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.052850+0800 Interview04-線程同步[986:58917] 還剩6張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.053069+0800 Interview04-線程同步[986:58917] 還剩5張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.194761+0800 Interview04-線程同步[986:58919] 還剩4張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195136+0800 Interview04-線程同步[986:58919] 還剩3張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195393+0800 Interview04-線程同步[986:58919] 還剩2張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195626+0800 Interview04-線程同步[986:58919] 還剩1張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195803+0800 Interview04-線程同步[986:58919] 還剩0張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}

OSSpinLock總結(jié)
  • OSSpinLock叫做”自旋鎖”,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài),一直占用著CPU資源

  • 目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題

  • 不安全,會(huì)根據(jù)優(yōu)先級(jí)反轉(zhuǎn),優(yōu)先級(jí)高的會(huì)先加鎖!

  • 優(yōu)先級(jí)反轉(zhuǎn) 如果線程2先進(jìn)去,線程1的優(yōu)先級(jí)比較高,那么CPU會(huì)更線程1分配大量資源,導(dǎo)致線程2資源很好,然后線程2就無(wú)法解鎖,會(huì)導(dǎo)致死鎖現(xiàn)象

os_unfair_lock
  • os_unfair_lock用于取代不安全的OSSpinLock ,從iOS10開(kāi)始才支持
  • 從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài),并非忙等
  • 需要導(dǎo)入頭文件#import <os/lock.h>
 os_unfair_lock lock = OS_UNFAIR_LOCK_INIT
 os_unfair_lock_trylock(&lock);//嘗試加鎖
 os_unfair_lock_lock(& lock);//加鎖
 os_unfair_lock_unlock(& lock); //解鎖

os_unfair_lock的特點(diǎn)等不到鎖就休眠

pthread_mutex
  • pthread開(kāi)頭的是垮平臺(tái)的
  • mutex叫做”互斥鎖”,等待鎖的線程會(huì)處于休眠狀態(tài)
  • 需要導(dǎo)入頭文件#import <pthread.h>
  pthread_mutex_t mutex = PTHREAD_MUTEX_DEFAULT;
 // #define PTHREAD_MUTEX_ERRORCHECK        1 //排錯(cuò)鎖
 // #define PTHREAD_MUTEX_RECURSIVE     2 //遞歸鎖
// #define PTHREAD_MUTEX_DEFAULT         //默認(rèn) PTHREAD_MUTEX_NORMAL

    // 初始化屬性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    // 初始化鎖
    pthread_mutex_init(mutex, &attr);
    // 銷毀屬性
    pthread_mutexattr_destroy(&attr);


- (void)ticket
{
    pthread_mutex_lock(&_ticketMutex); //加鎖
        執(zhí)行的代碼
     pthread_mutex_unlock(&_ticketMutex); //解鎖
}


  • 默認(rèn)鎖只能加一次鎖
pthread_mutex 遞歸鎖
  pthread_mutex_t mutex = PTHREAD_MUTEX_RECURSIVE;
  // 初始化屬性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    // 初始化鎖
    pthread_mutex_init(mutex, &attr);
    // 銷毀屬性
    pthread_mutexattr_destroy(&attr);
//比如調(diào)用頻繁這樣調(diào)用遞歸
- (void)otherTest
{
    pthread_mutex_lock(&_mutex);
    
    NSLog(@"%s", __func__);
    
    static int count = 0;
    if (count < 10) {
        count++;
        [self otherTest];
    }
    
    pthread_mutex_unlock(&_mutex);
}
  • 遞歸鎖 允許同一線程可以多次加鎖(不同線程 不允許這樣就可以保護(hù)線程)
NSLock ,NSRecursiveLock
  • NSLock是對(duì)mutex普通鎖的封裝
  • NSRecursiveLock也是對(duì)mutex遞歸鎖的封裝,API跟NSLock基本一致
@property (strong, nonatomic) NSLock *ticketLock;

- (instancetype)init
{
    if (self = [super init]) {
        self.ticketLock = [[NSLock alloc] init];
    }
    return self;
}

- (void)saleTicket
{
    [self.ticketLock lock];  加鎖
    
    加鎖的內(nèi)容代碼
    
    [self.ticketLock unlock];  解鎖
}
NSCondition
  • NSCondition是對(duì)mutex和cond的封裝

/**  系統(tǒng)提供的方法*/
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;

@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
- (instancetype)init
{
    if (self = [super init]) {
        self.condition = [[NSCondition alloc] init];
        self.data = [NSMutableArray array];
    }
    return self;
}

// 生產(chǎn)者-消費(fèi)者模式

// 線程1
// 刪除數(shù)組中的元素
- (void)__remove
{
    [self.condition lock];
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        [self.condition wait];
    }
    
    [self.data removeLastObject];
    NSLog(@"刪除了元素");
    
    [self.condition unlock];
}

// 線程2
// 往數(shù)組中添加元素
- (void)__add
{
    [self.condition lock];
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    // 信號(hào)
    [self.condition signal];
    
    // 廣播
//    [self.condition broadcast];
    [self.condition unlock];
    
}

NSConditionLock
  • NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝,可以設(shè)置具體的條件值
  • 條件鎖
/** 系統(tǒng)提供方法 */
- (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;


現(xiàn)在我想執(zhí)行__one 執(zhí)行線程 __two  __three

@property (strong, nonatomic) NSConditionLock *conditionLock;
- (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];
}

- (void)__three
{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

dispatch_queue(DISPATCH_QUEUE_SERIAL)
@property (strong, nonatomic) dispatch_queue_t ticketQueue;

- (instancetype)init
{
    if (self = [super init]) {
        self.ticketQueue = dispatch_queue_create("ticketQueue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)__saleTicket
{
    dispatch_sync(self.ticketQueue, ^{
       代碼
    });
}

dispatch_semaphore_t 信號(hào)量
  • semaphore叫做”信號(hào)量”
  • 信號(hào)量的初始值,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量
  • 信號(hào)量的初始值為1,代表同時(shí)只允許1條線程訪問(wèn)資源,保證線程同步

// 信號(hào)量初始值
 int value = 1;
//初始化信號(hào)量
@property (strong, nonatomic) dispatch_semaphore_t ticketSemaphore;

- (instancetype)init
{
    if (self = [super init]) {
        self.ticketSemaphore = dispatch_semaphore_create(1);
   }
    return self;
}

// 線程10、7、6、9、8
- (void)test
{
    // 如果信號(hào)量的值 > 0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
    // 如果信號(hào)量的值 <= 0,就會(huì)休眠等待,直到信號(hào)量的值變成>0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    
    sleep(2);
    NSLog(@"test - %@", [NSThread currentThread]);
    
    // 讓信號(hào)量的值+1
    dispatch_semaphore_signal(self.semaphore);
}

?著作權(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)容

  • 前提簡(jiǎn)述: 常用的線程方案有Pthread,NSThread, GCD,NSOperation。以下是比較:pth...
    小小小蚍蜉閱讀 2,807評(píng)論 0 7
  • 鎖是一種同步機(jī)制,用于多線程環(huán)境中對(duì)資源訪問(wèn)的限制iOS中常見(jiàn)鎖的性能對(duì)比圖(摘自:ibireme): iOS鎖的...
    LiLS閱讀 1,625評(píng)論 0 6
  • 多線程安全問(wèn)題 多個(gè)線程可能訪問(wèn)同一塊資源,比如同一個(gè)文件,同一個(gè)對(duì)象,同一個(gè)變量等;當(dāng)多個(gè)線程訪問(wèn)同一資源時(shí),容...
    _小沫閱讀 1,118評(píng)論 0 2
  • 心情有些許小小的激動(dòng)。練了一下午的舞蹈,總算有點(diǎn)進(jìn)展。腦子有些混亂,一會(huì)兒感到緊張,一會(huì)兒又有些驕傲,簡(jiǎn)直精神...
    欣_018a閱讀 207評(píng)論 1 5
  • 樹(shù)莓派學(xué)習(xí)記錄 - 2019-01-19 安裝系統(tǒng) 1. 下載樹(shù)莓派系統(tǒng) 推薦直接在官網(wǎng)下載Raspbianhtt...
    laughing_8c3d閱讀 767評(píng)論 0 1

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