iOS多线程(6)-Lock

  • 为什么用锁:

    多线程编程中,如果对同一数据源进行读写操作就会造成不可预知的结果,所以我们应该尽量避免并发操作资源在线程之间共享,以减少线程间的相互作用,就需要一些同步工具,来确保当它们交互的时候是安全的。

  • 锁的种类:

    iOS开发中常用的锁有如下几种:

    1. @synchronized 同步锁
    2. NSLock 对象锁
    3. NSRecursiveLock 递归锁
    4. NSConditionLock 条件锁
    5. pthread_mutex 互斥锁(C语言)
    6. dispatch_semaphore 信号量实现加锁(GCD)
    7. OSSpinLock 自旋锁 (暂不建议使用,原因参见这里
  • 锁的性能对比:

    我们先来看一下网上的评测,几乎关于锁性能介绍的文章都用了这张图:
    work_time.png

    那根据这张图所描述的耗时对别正确吗?我们自己实际测试一下,看下边的测试代码:

    #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
    

    看输出结果一目了然了,跟上图的评测结果一致。

  • 如何使用锁:

    1. @synchronized卖票例子:
    - (void)sycnAction {
      __block int ticketsCount = 5;
      
      void(^saleTickets)(void) = ^() {
          while (YES) {
              /*
               注意点:
               1.加锁的代码尽量少
               2.添加的OC对象必须在多个线程中都是同一对象
               3.优点是不需要显式的创建锁对象,便可以实现锁的机制。
               4.@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。
               */
              
              //这里参数添加一个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(@"任务2");
          
          pthread_mutex_unlock(&mutex);
      });
      
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          pthread_mutex_lock(&mutex);
          NSLog(@"任务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();
      });
    }
    
  • 死锁:待续....

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容