iOS多線程(6)-Lock

  • 為什么用鎖:

    多線程編程中知纷,如果對同一數(shù)據(jù)源進行讀寫操作就會造成不可預知的結(jié)果,所以我們應(yīng)該盡量避免并發(fā)操作資源在線程之間共享是辕,以減少線程間的相互作用眉反,就需要一些同步工具狞谱,來確保當它們交互的時候是安全的。

  • 鎖的種類:

    iOS開發(fā)中常用的鎖有如下幾種:

    1. @synchronized 同步鎖
    2. NSLock 對象鎖
    3. NSRecursiveLock 遞歸鎖
    4. NSConditionLock 條件鎖
    5. pthread_mutex 互斥鎖(C語言)
    6. dispatch_semaphore 信號量實現(xiàn)加鎖(GCD)
    7. OSSpinLock 自旋鎖 (暫不建議使用禁漓,原因參見這里
  • 鎖的性能對比:

    我們先來看一下網(wǎng)上的評測跟衅,幾乎關(guān)于鎖性能介紹的文章都用了這張圖:
    work_time.png

    那根據(jù)這張圖所描述的耗時對別正確嗎?我們自己實際測試一下播歼,看下邊的測試代碼:

    #import <pthread.h>
    #import <libkern/OSAtomic.h>
    
    #define CycleTime (1024*1024*32)
    
    - (void)testAction {
      NSTimeInterval time = [NSDate date].timeIntervalSince1970;
      
      //同步鎖
      for (int i = 0; i < CycleTime; i++) {
          @synchronized(self) {
              
          }
      }
      NSLog(@"%f : work time of Synchronized",[NSDate date].timeIntervalSince1970-time);
      time = [NSDate date].timeIntervalSince1970;
      
      //條件鎖
      NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
      for (int i = 0; i < CycleTime; i++) {
          [conditionLock lock];
          [conditionLock unlock];
      }
      NSLog(@"%f : work time of NSConditionLock",[NSDate date].timeIntervalSince1970-time);
      time = [NSDate date].timeIntervalSince1970;
      
      //遞歸鎖
      NSRecursiveLock *rsLock = [[NSRecursiveLock alloc] init];
      for (int i = 0; i < CycleTime; i++) {
          [rsLock lock];
          [rsLock unlock];
      }
      NSLog(@"%f : work time of NSRecursiveLock",[NSDate date].timeIntervalSince1970-time);
      time = [NSDate date].timeIntervalSince1970;
      
      //互斥鎖
      NSLock *mutexLock = [[NSLock alloc] init];
      for (int i = 0; i < CycleTime; i++) {
          [mutexLock lock];
          [mutexLock unlock];
      }
      NSLog(@"%f : work time of NSLock",[NSDate date].timeIntervalSince1970-time);
      time = [NSDate date].timeIntervalSince1970;
      
      //互斥鎖
      pthread_mutex_t mutex;
      pthread_mutex_init(&mutex, NULL);
      for (int i = 0; i < CycleTime; i++) {
          pthread_mutex_lock(&mutex);
          pthread_mutex_unlock(&mutex);
      }
      NSLog(@"%f : work time of pthread_mutex",[NSDate date].timeIntervalSince1970-time);
      time = [NSDate date].timeIntervalSince1970;
      
      //信號量
      dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
      for (int i = 0; i < CycleTime; i++) {
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          dispatch_semaphore_signal(semaphore);
      }
      NSLog(@"%f : work time of dispatch_semaphore",[NSDate date].timeIntervalSince1970-time);
      time = [NSDate date].timeIntervalSince1970;
      
      //自旋鎖
      OSSpinLock spinlock = OS_SPINLOCK_INIT;
      for (int i = 0; i < CycleTime; i++) {
          OSSpinLockLock(&spinlock);
          OSSpinLockUnlock(&spinlock);
      }
      NSLog(@"%f : work time of OSSpinLock",[NSDate date].timeIntervalSince1970-time);
    }
    

    輸出:

    2018-03-29 18:51:14.638390+0800 Lock[97416:4313533] 3.498765 : work time of Synchronized
    2018-03-29 18:51:16.987938+0800 Lock[97416:4313533] 2.349282 : work time of NSConditionLock
    2018-03-29 18:51:18.480868+0800 Lock[97416:4313533] 1.492687 : work time of NSRecursiveLock
    2018-03-29 18:51:19.286578+0800 Lock[97416:4313533] 0.805475 : work time of NSLock
    2018-03-29 18:51:20.029690+0800 Lock[97416:4313533] 0.742872 : work time of pthread_mutex
    2018-03-29 18:51:20.556899+0800 Lock[97416:4313533] 0.526966 : work time of dispatch_semaphore
    2018-03-29 18:51:20.909306+0800 Lock[97416:4313533] 0.352188 : work time of OSSpinLock
    

    看輸出結(jié)果一目了然了伶跷,跟上圖的評測結(jié)果一致掰读。

  • 如何使用鎖:

    1. @synchronized賣票例子:
    - (void)sycnAction {
      __block int ticketsCount = 5;
      
      void(^saleTickets)(void) = ^() {
          while (YES) {
              /*
               注意點:
               1.加鎖的代碼盡量少
               2.添加的OC對象必須在多個線程中都是同一對象
               3.優(yōu)點是不需要顯式的創(chuàng)建鎖對象,便可以實現(xiàn)鎖的機制叭莫。
               4.@synchronized塊會隱式的添加一個異常處理例程來保護代碼蹈集,該處理例程會在異常拋出的時候自動的釋放互斥鎖雇初。所以如果不想讓隱式的異常處理例程帶來額外的開銷,你可以考慮使用鎖對象靖诗。
               */
              
              //這里參數(shù)添加一個OC對象,一般使用self
              @synchronized(self) {
                  [NSThread sleepForTimeInterval:1];
                  if (ticketsCount > 0) {
                      ticketsCount--;
                      NSLog(@"Tickets = %d, Thread:%@",ticketsCount,[NSThread currentThread]);
                  }
                  else {
                      NSLog(@"Clear  Thread:%@",[NSThread currentThread]);
                      break;
                  }
              }
          }
      };
      
      NSLog(@"Start tickets count = %d, Thread:%@",ticketsCount,[NSThread currentThread]);
    
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          saleTickets();
      });
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          saleTickets();
      });
    }
    
    1. NSLock互斥鎖賣票例子:
    - (void)lockAction {
      __block int ticketsCount = 5;
      
      NSLock *lock = [[NSLock alloc] init];
      
      void(^lockSaleTickets)(void) = ^() {
          while (1) {
              [lock lock];
              
              [NSThread sleepForTimeInterval:1];
              if (ticketsCount > 0) {
                  ticketsCount--;
                  NSLog(@"Tickets= %d, Thread:%@",ticketsCount,[NSThread currentThread]);
              }
              else {
                  NSLog(@"Clear  Thread:%@",[NSThread currentThread]);
                  break;
              }
              
              [lock unlock];
          }
      };
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          lockSaleTickets();
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          lockSaleTickets();
      });
    }
    
    1. NSRecursiveLock遞歸鎖賣票例子:
    - (void)recursiveLockAction {
    //    NSLock *lock = [[NSLock alloc] init];
      NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          static void(^TestMethod)(int);
          
          TestMethod = ^(int value) {
              [lock lock];
              if (value > 0) {
                  NSLog(@"value = %d",value);
                  [NSThread sleepForTimeInterval:1];
                  TestMethod(--value);
              }
              [lock unlock];
          };
          
          NSLog(@"Begain Test");
          TestMethod(5);
      });
    }
    
    1. NSConditionLock條件鎖賣票例子:
    - (void)conditionLockActive {
      NSConditionLock *lock = [[NSConditionLock alloc] init];
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          for (int i = 0; i <= 5; i++) {
              [lock lock];
              NSLog(@"1.thread1 condition = %ld, i = %d",(long)lock.condition,i);
              [lock unlockWithCondition:i];
              NSLog(@"2.thread1 condition = %ld, i = %d",(long)lock.condition,i);
          }
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
          [lock lockWhenCondition:2];
          NSLog(@"thread2");
          [lock unlock];
      });
    }
    
    1. pthread_mutex_t互斥鎖例子:
    - (void)pthreadMutexAction {
      __block pthread_mutex_t mutex;
      pthread_mutex_init(&mutex, NULL);
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          sleep(2);
          pthread_mutex_lock(&mutex);
          NSLog(@"任務(wù)2");
          
          pthread_mutex_unlock(&mutex);
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          pthread_mutex_lock(&mutex);
          NSLog(@"任務(wù)1");
          pthread_mutex_unlock(&mutex);
      });
    }
    
    1. dispatch_semaphore_t信號量例子:
    - (void)semaphoreAction {
      dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
      
      __block int count = 10;
      
      void(^TestMethod)(void) = ^() {
          while (YES) {
              dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
              if (count > 0) {
                  count --;
                  NSLog(@"value = %d, %@",count,[NSThread currentThread]);
                  [NSThread sleepForTimeInterval:1];
              }
              else {
                  NSLog(@"Done %@",[NSThread currentThread]);
                  break;
              }
              dispatch_semaphore_signal(semaphore);
          }
      };
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          TestMethod();
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          TestMethod();
      });
    }
    
    1. OSSpinLock自旋鎖例子:
    - (void)spinAction {
      __block int tickets = 5;
      
      OSSpinLock spinlock = OS_SPINLOCK_INIT;
      
      void(^TestMethod)(void) = ^() {
          while (YES) {
              OSSpinLockLock(&spinlock);
              if (tickets > 0) {
                  tickets --;
                  NSLog(@"value = %d, %@",tickets,[NSThread currentThread]);
                  [NSThread sleepForTimeInterval:1];
              }
              else {
                  NSLog(@"Done %@",[NSThread currentThread]);
                  break;
              }
              OSSpinLockUnlock(&spinlock);
          }
      };
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          TestMethod();
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          TestMethod();
      });
    }
    
  • 死鎖:待續(xù)....

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖浓冒,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尖坤,死亡現(xiàn)場離奇詭異,居然都是意外死亡糖驴,警方通過查閱死者的電腦和手機佛致,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門贮缕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俺榆,你說我怎么就攤上這事感昼。” “怎么了罐脊?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵定嗓,是天一觀的道長。 經(jīng)常有香客問我萍桌,道長宵溅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任上炎,我火速辦了婚禮恃逻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己寇损,他們只是感情好凸郑,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著矛市,像睡著了一般芙沥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浊吏,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天而昨,我揣著相機與錄音,去河邊找鬼卿捎。 笑死配紫,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的午阵。 我是一名探鬼主播躺孝,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼底桂!你這毒婦竟也來了植袍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤籽懦,失蹤者是張志新(化名)和其女友劉穎于个,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體暮顺,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡厅篓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捶码。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羽氮。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖惫恼,靈堂內(nèi)的尸體忽然破棺而出档押,到底是詐尸還是另有隱情,我是刑警寧澤祈纯,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站粒没,受9級特大地震影響簇爆,放射性物質(zhì)發(fā)生泄漏倾贰。R本人自食惡果不足惜匆浙,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一首尼、第九天 我趴在偏房一處隱蔽的房頂上張望言秸。 院中可真熱鬧,春花似錦举畸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至率挣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間椒功,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工丁屎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谦炬,地道東北人节沦。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓甫贯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叫搁。 傳聞我的和親對象是個殘疾皇子供炎,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容