iOS底層分析 - 線程鎖(三)條件鎖 NSCondition

條件鎖介紹
應(yīng)用
源碼

NSCondition

條件鎖

條件鎖我們調(diào)用wait方法就把當(dāng)前線程進(jìn)入等待狀態(tài),當(dāng)調(diào)用了signal方法就可以讓該線程繼續(xù)執(zhí)行,也可以調(diào)用broadcast廣播方法。

NSCondition 和 NSLock 的區(qū)別
NSLock:在獲取不到鎖的時(shí)候自動(dòng)使線程進(jìn)入休眠,鎖被釋放后線程自動(dòng)被喚醒
NSCondition:可以使我們更加靈活的控制線程狀態(tài),在任何需要的時(shí)候使線程進(jìn)入休眠或喚醒它

NSCondition 遵循 NSLocking 協(xié)議

@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}

  • (void)wait;//休眠
  • (BOOL)waitUntilDate:(NSDate *)limit;//休眠到什么時(shí)間
  • (void)signal;//發(fā)送信號(hào)喚醒鎖
  • (void)broadcast;//廣播喚醒鎖

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

@end
-wait 阻塞當(dāng)期線程,使線程進(jìn)入休眠,等待喚醒信號(hào)。調(diào)用前必須已加鎖 -lock
-waitUntilDate 阻塞當(dāng)前線程,線程進(jìn)入休眠,等待喚醒信號(hào)或者超時(shí)。如果是被信號(hào)喚醒返回YES,否者返回NO。調(diào)用前必須已加鎖 -lock
-signal 喚醒一個(gè)正在休眠的線程,如果要喚醒多個(gè)線程,需要調(diào)用多次,如果沒有線程在等待,什么也不做。調(diào)用前必須已加鎖 -lock
-broadcast 喚醒所有在等待的線程,如果沒有線程在等待,什么也不做。調(diào)用前必須已加鎖 -lock

Condition 是條件,條件是我們自己決定的。

應(yīng)用

@property(nonatomic, strong) NSCondition * testCondition;
@property(nonatomic, assign) NSUInteger ticketCount;

@end

@implementation LJLLockViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    self.ticketCount = 0;
    [self ljl_testCondition];
    }
    -(void)ljl_testCondition
    {
    _testCondition = [[NSCondition alloc] init];
    // 創(chuàng)建生產(chǎn) - 消費(fèi)者
    for (int i=0; i<50; i++) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [self ljl_producer];
    });

      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
          [self ljl_consumer];
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
          [self ljl_consumer];
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
          [self ljl_producer];
      });
    

    }
    }

-(void)ljl_producer
{
[_testCondition lock];
self.ticketCount = self.ticketCount + 1;
NSLog(@"生產(chǎn)一個(gè) 現(xiàn)有 count %zd",self.ticketCount);
[_testCondition signal];//信號(hào),生產(chǎn)了一個(gè)通知可以消費(fèi)了
[_testCondition unlock];//解鎖
}

-(void)ljl_consumer
{
// 線程安全
[_testCondition lock];

// if (self.ticketCount == 0) {
while (self.ticketCount == 0) {
NSLog(@"等待 count %zd",self.ticketCount);
// 保證正常流程
[_testCondition wait];//等待 剩余的count == 0 不能再消費(fèi)所以要等待
}

// 注意消費(fèi)行為,要等待條件判斷之后
self.ticketCount -= 1;
NSLog(@"消費(fèi)一個(gè) 還剩 count %zd",self.ticketCount);
[_testCondition unlock];
}
if 不安全,因?yàn)橛卸嗑€程所以并不能卡住所有的判斷條件。同時(shí)也沒加鎖的情況下 就可以看到先出現(xiàn)了消費(fèi)1個(gè)還剩0個(gè),案例說 ==0了應(yīng)該卡住等待,結(jié)果有打印了1

2020-04-04 21:09:33.386969+0800 filedome[47414:1674928] 生產(chǎn)一個(gè) 現(xiàn)有 count 1
2020-04-04 21:09:33.387002+0800 filedome[47414:1674486] 生產(chǎn)一個(gè) 現(xiàn)有 count 2
2020-04-04 21:09:33.387111+0800 filedome[47414:1674928] 消費(fèi)一個(gè) 還剩 count 0
2020-04-04 21:09:33.387111+0800 filedome[47414:1674927] 消費(fèi)一個(gè) 還剩 count 1
2020-04-04 21:09:33.387116+0800 filedome[47414:1674926] 等待 count 0
換成while 雖然能卡住但是 == 0,但是不能處理多線程同時(shí)訪問的問題。

2020-04-04 21:11:54.433800+0800 filedome[47526:1676373] 消費(fèi)一個(gè) 還剩 count 0
2020-04-04 21:11:54.498035+0800 filedome[47526:1676429] 消費(fèi)一個(gè) 還剩 count 0
2020-04-04 21:11:54.498046+0800 filedome[47526:1676385] 生產(chǎn)一個(gè) 現(xiàn)有 count 1
2020-04-04 21:11:54.498198+0800 filedome[47526:1676387] 生產(chǎn)一個(gè) 現(xiàn)有 count 2

加條件鎖 NSCondition
ticketCount == 0 的時(shí)候休眠 (-wait),ticketCount>0 的時(shí)候發(fā)信號(hào)喚醒線程進(jìn)行執(zhí)行 (-signal)

源碼

NSCondition 源碼。其中使用了一個(gè)pthread_mutex_t 的互斥鎖(同NSLock),還使用了 pthread_cond_t 的條件共同實(shí)現(xiàn)。

open func wait() {
    pthread_cond_wait(cond, mutex)
}

open func wait(until limit: Date) -> Bool {
    guard var timeout = timeSpecFrom(date: limit) else {
        return false
    }
    return pthread_cond_timedwait(cond, mutex, &timeout) == 0
}

open func signal() {
    pthread_cond_signal(cond)
}

open func broadcast() {
    pthread_cond_broadcast(cond)
}

NSCondition 就相當(dāng)于 NSLock + Condition,通過condition 可以隨時(shí)使線程進(jìn)入休眠或被喚醒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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